1
0
mirror of https://github.com/SuperBFG7/ympd synced 2024-06-25 22:23:16 +00:00

Save myMPD settings in state file, not in cookies

This commit is contained in:
jcorporation 2018-06-21 17:57:40 +01:00
parent b790a687a3
commit c11bfa0d1a
9 changed files with 716 additions and 391 deletions

View File

@ -687,7 +687,6 @@
</div><!-- /.modal -->
<script src="js/jquery-3.3.1.min.js"></script>
<script src="js/js.cookie-2.2.0.min.js"></script>
<script src="js/bootstrap.bundle.min.js"></script>
<script src="js/bootstrap-slider.min.js"></script>
<script src="js/bootstrap-notify.min.js"></script>

View File

@ -1,165 +0,0 @@
/*!
* JavaScript Cookie v2.2.0
* https://github.com/js-cookie/js-cookie
*
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack
* Released under the MIT license
*/
;(function (factory) {
var registeredInModuleLoader = false;
if (typeof define === 'function' && define.amd) {
define(factory);
registeredInModuleLoader = true;
}
if (typeof exports === 'object') {
module.exports = factory();
registeredInModuleLoader = true;
}
if (!registeredInModuleLoader) {
var OldCookies = window.Cookies;
var api = window.Cookies = factory();
api.noConflict = function () {
window.Cookies = OldCookies;
return api;
};
}
}(function () {
function extend () {
var i = 0;
var result = {};
for (; i < arguments.length; i++) {
var attributes = arguments[ i ];
for (var key in attributes) {
result[key] = attributes[key];
}
}
return result;
}
function init (converter) {
function api (key, value, attributes) {
var result;
if (typeof document === 'undefined') {
return;
}
// Write
if (arguments.length > 1) {
attributes = extend({
path: '/'
}, api.defaults, attributes);
if (typeof attributes.expires === 'number') {
var expires = new Date();
expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
attributes.expires = expires;
}
// We're using "expires" because "max-age" is not supported by IE
attributes.expires = attributes.expires ? attributes.expires.toUTCString() : '';
try {
result = JSON.stringify(value);
if (/^[\{\[]/.test(result)) {
value = result;
}
} catch (e) {}
if (!converter.write) {
value = encodeURIComponent(String(value))
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
} else {
value = converter.write(value, key);
}
key = encodeURIComponent(String(key));
key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
key = key.replace(/[\(\)]/g, escape);
var stringifiedAttributes = '';
for (var attributeName in attributes) {
if (!attributes[attributeName]) {
continue;
}
stringifiedAttributes += '; ' + attributeName;
if (attributes[attributeName] === true) {
continue;
}
stringifiedAttributes += '=' + attributes[attributeName];
}
return (document.cookie = key + '=' + value + stringifiedAttributes);
}
// Read
if (!key) {
result = {};
}
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling "get()"
var cookies = document.cookie ? document.cookie.split('; ') : [];
var rdecode = /(%[0-9A-Z]{2})+/g;
var i = 0;
for (; i < cookies.length; i++) {
var parts = cookies[i].split('=');
var cookie = parts.slice(1).join('=');
if (!this.json && cookie.charAt(0) === '"') {
cookie = cookie.slice(1, -1);
}
try {
var name = parts[0].replace(rdecode, decodeURIComponent);
cookie = converter.read ?
converter.read(cookie, name) : converter(cookie, name) ||
cookie.replace(rdecode, decodeURIComponent);
if (this.json) {
try {
cookie = JSON.parse(cookie);
} catch (e) {}
}
if (key === name) {
result = cookie;
break;
}
if (!key) {
result[name] = cookie;
}
} catch (e) {}
}
return result;
}
api.set = api;
api.get = function (key) {
return api.call(api, key);
};
api.getJSON = function () {
return api.apply({
json: true
}, [].slice.call(arguments));
};
api.defaults = {};
api.remove = function (key, attributes) {
api(key, '', extend(attributes, {
expires: -1
}));
};
api.withConverter = init;
return api;
}
return init(function () {});
}));

View File

@ -1,3 +0,0 @@
/*! js-cookie v2.2.0 | MIT */
!function(e){var n=!1;if("function"==typeof define&&define.amd&&(define(e),n=!0),"object"==typeof exports&&(module.exports=e(),n=!0),!n){var o=window.Cookies,t=window.Cookies=e();t.noConflict=function(){return window.Cookies=o,t}}}(function(){function e(){for(var e=0,n={};e<arguments.length;e++){var o=arguments[e];for(var t in o)n[t]=o[t]}return n}function n(o){function t(n,r,i){var c;if("undefined"!=typeof document){if(arguments.length>1){if("number"==typeof(i=e({path:"/"},t.defaults,i)).expires){var a=new Date;a.setMilliseconds(a.getMilliseconds()+864e5*i.expires),i.expires=a}i.expires=i.expires?i.expires.toUTCString():"";try{c=JSON.stringify(r),/^[\{\[]/.test(c)&&(r=c)}catch(e){}r=o.write?o.write(r,n):encodeURIComponent(r+"").replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),n=(n=(n=encodeURIComponent(n+"")).replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent)).replace(/[\(\)]/g,escape);var s="";for(var f in i)i[f]&&(s+="; "+f,!0!==i[f]&&(s+="="+i[f]));return document.cookie=n+"="+r+s}n||(c={});for(var p=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,u=0;u<p.length;u++){var l=p[u].split("="),C=l.slice(1).join("=");this.json||'"'!==C.charAt(0)||(C=C.slice(1,-1));try{var m=l[0].replace(d,decodeURIComponent);if(C=o.read?o.read(C,m):o(C,m)||C.replace(d,decodeURIComponent),this.json)try{C=JSON.parse(C)}catch(e){}if(n===m){c=C;break}n||(c[m]=C)}catch(e){}}return c}}return t.set=t,t.get=function(e){return t.call(t,e)},t.getJSON=function(){return t.apply({json:!0},[].slice.call(arguments))},t.defaults={},t.remove=function(n,o){t(n,"",e(o,{expires:-1}))},t.withConverter=n,t}return n(function(){})});

View File

@ -27,13 +27,12 @@ var last_song = '';
var last_state;
var last_outputs;
var current_song = new Object();
var MAX_ELEMENTS_PER_PAGE = 100;
var isTouch = Modernizr.touch ? 1 : 0;
var playstate = '';
var progressBar;
var volumeBar;
var coverImageFile = '';
var settings = { "notifyPage":1, "notifyWeb": 0, "mpdstream":"" };
var settings = {};
var app = {};
@ -214,8 +213,7 @@ app.route=function() {
};
$(document).ready(function(){
sendAPI({"cmd":"MPD_API_GET_MPD_SETTINGS"},parseSettings);
sendAPI({"cmd":"MPD_API_GET_MYMPD_SETTINGS"},parseMympdSettings);
sendAPI({"cmd":"MPD_API_GET_SETTINGS"},parseSettings);
sendAPI({"cmd":"MPD_API_GET_OUTPUTNAMES"},parseOutputnames);
webSocketConnect();
@ -257,14 +255,9 @@ $(document).ready(function(){
$('#search > input').focus();
});
if(!notificationsSupported())
$('#btnnotifyWeb').addClass("disabled");
else
if (Cookies.get('notificationWeb') === 'true')
$('#btnnotifyWeb').removeClass('btn-secondary').addClass("btn-success")
if (Cookies.get('notificationPage') === 'true')
$('#btnnotifyPage').removeClass('btn-secondary').addClass("btn-success")
add_filter('#BrowseFilesystemFilterLetters');
add_filter('#BrowseDatabaseFilterLetters');
@ -311,9 +304,6 @@ function webSocketConnect() {
case "song_change":
songChange(obj);
break;
case 'settings':
parseSettings(obj);
break;
case 'error':
showNotification(obj.data,'','','danger');
default:
@ -373,55 +363,73 @@ function parseStats(obj) {
$('#mpdVersion').text(obj.data.mpd_version);
}
function parseMympdSettings(obj) {
settings.notifyPage=obj.data.notifyPage;
settings.notifyWeb=obj.data.notifyWeb;
//try to set web notification
}
function parseSettings(obj) {
if (!isNaN(obj.data.max_elements_per_page))
MAX_ELEMENTS_PER_PAGE=obj.data.max_elements_per_page;
if(obj.data.random)
$('#btnrandom').removeClass('btn-secondary').addClass("btn-success")
else
$('#btnrandom').removeClass("btn-success").addClass("btn-secondary");
if (obj.data.random)
$('#btnrandom').removeClass('btn-secondary').addClass("btn-success")
else
$('#btnrandom').removeClass("btn-success").addClass("btn-secondary");
if(obj.data.consume)
$('#btnconsume').removeClass('btn-secondary').addClass("btn-success")
else
$('#btnconsume').removeClass("btn-success").addClass("btn-secondary");
if (obj.data.consume)
$('#btnconsume').removeClass('btn-secondary').addClass("btn-success")
else
$('#btnconsume').removeClass("btn-success").addClass("btn-secondary");
if(obj.data.single)
$('#btnsingle').removeClass('btn-secondary').addClass("btn-success")
else
$('#btnsingle').removeClass("btn-success").addClass("btn-secondary");
if (obj.data.single)
$('#btnsingle').removeClass('btn-secondary').addClass("btn-success")
else
$('#btnsingle').removeClass("btn-success").addClass("btn-secondary");
if(obj.data.crossfade != undefined)
$('#inputCrossfade').removeAttr('disabled').val(obj.data.crossfade);
else
$('#inputCrossfade').attr('disabled', 'disabled');
if (obj.data.crossfade != undefined)
$('#inputCrossfade').removeAttr('disabled').val(obj.data.crossfade);
else
$('#inputCrossfade').attr('disabled', 'disabled');
if(obj.data.mixrampdb != undefined)
$('#inputMixrampdb').removeAttr('disabled').val(obj.data.mixrampdb);
else
$('#inputMixrampdb').attr('disabled', 'disabled');
if (obj.data.mixrampdb != undefined)
$('#inputMixrampdb').removeAttr('disabled').val(obj.data.mixrampdb);
else
$('#inputMixrampdb').attr('disabled', 'disabled');
if(obj.data.mixrampdelay != undefined)
$('#inputMixrampdelay').removeAttr('disabled').val(obj.data.mixrampdelay);
else
$('#inputMixrampdb').attr('disabled', 'disabled');
if (obj.data.mixrampdelay != undefined)
$('#inputMixrampdelay').removeAttr('disabled').val(obj.data.mixrampdelay);
else
$('#inputMixrampdb').attr('disabled', 'disabled');
if(obj.data.repeat)
$('#btnrepeat').removeClass('btn-secondary').addClass("btn-success")
else
$('#btnrepeat').removeClass("btn-success").addClass("btn-secondary");
if (obj.data.repeat)
$('#btnrepeat').removeClass('btn-secondary').addClass("btn-success")
else
$('#btnrepeat').removeClass("btn-success").addClass("btn-secondary");
$("#selectReplaygain").val(obj.data.replaygain);
$("#selectReplaygain").val(obj.data.replaygain);
if (notificationsSupported()) {
if (obj.data.notificationWeb) {
$('#btnnotifyWeb').removeClass('btn-secondary').addClass("btn-success")
Notification.requestPermission(function (permission) {
if(!('permission' in Notification))
Notification.permission = permission;
if (permission === 'granted') {
$('#btnnotifyWeb').removeClass('btn-secondary').addClass('btn-success');
} else {
$('#btnnotifyWeb').addClass('btn-secondary').removeClass('btn-success');
obj.data.notificationWeb=0;
}
});
}
else
$('#btnnotifyWeb').addClass('btn-secondary').removeClass("btn-success");
} else {
$('#btnnotifyWeb').addClass("disabled");
$('#btnnotifyWeb').addClass('btn-secondary').removeClass("btn-success");
}
if (obj.data.notificationPage)
$('#btnnotifyPage').removeClass('btn-secondary').addClass("btn-success")
else
$('#btnnotifyPage').addClass('btn-secondary').removeClass("btn-success");
settings=obj.data;
setLocalStream(obj.data.mpdhost,obj.data.streamport);
coverImageFile=obj.data.coverimage;
}
function parseOutputnames(obj) {
@ -824,7 +832,7 @@ function parseListDBtags(obj) {
function parseListTitles(obj) {
if(app.current.app !== 'Browse' && app.current.tab !== 'Database' && app.current.view !== 'Album') return;
var album=$('#'+genId(obj.album)+' > div > table > tbody');
$('#'+genId(obj.album)+' > img').attr('src','/library/'+obj.data[0].uri.replace(/\/[^\/]+$/,'\/')+coverImageFile);
$('#'+genId(obj.album)+' > img').attr('src','/library/'+obj.data[0].uri.replace(/\/[^\/]+$/,'\/')+settings.coverimage);
$('#'+genId(obj.album)+' > img').attr('uri',obj.data[0].uri.replace(/\/[^\/]+$/,''));
$('#'+genId(obj.album)+' > img').attr('data-album',encodeURI(obj.album));
var titleList='';
@ -850,26 +858,26 @@ function parseListTitles(obj) {
}
function setPagination(number) {
var totalPages=Math.ceil(number / MAX_ELEMENTS_PER_PAGE);
var totalPages=Math.ceil(number / settings.max_elements_per_page);
var cat=app.current.app+(app.current.tab==undefined ? '': app.current.tab);
if (totalPages==0) { totalPages=1; }
$('#'+cat+'PaginationTopPage').text('Page '+(app.current.page / MAX_ELEMENTS_PER_PAGE + 1)+' / '+totalPages);
$('#'+cat+'PaginationBottomPage').text('Page '+(app.current.page / MAX_ELEMENTS_PER_PAGE + 1)+' / '+totalPages);
$('#'+cat+'PaginationTopPage').text('Page '+(app.current.page / settings.max_elements_per_page + 1)+' / '+totalPages);
$('#'+cat+'PaginationBottomPage').text('Page '+(app.current.page / settings.max_elements_per_page + 1)+' / '+totalPages);
if (totalPages > 1) {
$('#'+cat+'PaginationTopPage').removeClass('disabled').removeAttr('disabled');
$('#'+cat+'PaginationBottomPage').removeClass('disabled').removeAttr('disabled');
$('#'+cat+'PaginationTopPages').empty();
$('#'+cat+'PaginationBottomPages').empty();
for (var i=0;i<totalPages;i++) {
$('#'+cat+'PaginationTopPages').append('<button onclick="gotoPage('+(i * MAX_ELEMENTS_PER_PAGE)+',this,event)" type="button" class="mr-1 mb-1 btn-sm btn btn-secondary">'+(i+1)+'</button>');
$('#'+cat+'PaginationBottomPages').append('<button onclick="gotoPage('+(i * MAX_ELEMENTS_PER_PAGE)+',this,event)" type="button" class="mr-1 mb-1 btn-sm btn btn-secondary">'+(i+1)+'</button>');
$('#'+cat+'PaginationTopPages').append('<button onclick="gotoPage('+(i * settings.max_elements_per_page)+',this,event)" type="button" class="mr-1 mb-1 btn-sm btn btn-secondary">'+(i+1)+'</button>');
$('#'+cat+'PaginationBottomPages').append('<button onclick="gotoPage('+(i * settings.max_elements_per_page)+',this,event)" type="button" class="mr-1 mb-1 btn-sm btn btn-secondary">'+(i+1)+'</button>');
}
} else {
$('#'+cat+'PaginationTopPage').addClass('disabled').attr('disabled','disabled');
$('#'+cat+'PaginationBottomPage').addClass('disabled').attr('disabled','disabled');
}
if(number > app.current.page + MAX_ELEMENTS_PER_PAGE) {
if(number > app.current.page + settings.max_elements_per_page) {
$('#'+cat+'PaginationTopNext').removeClass('disabled').removeAttr('disabled');
$('#'+cat+'PaginationBottomNext').removeClass('disabled').removeAttr('disabled');
$('#'+cat+'ButtonsBottom').removeClass('hide');
@ -1013,10 +1021,16 @@ $('#btnconsume').on('click', function (e) {
$('#btnsingle').on('click', function (e) {
toggleBtn(this);
});
$('#btnrepeat').on('click', function (e) {
toggleBtn(this);
});
$('#btnnotifyPage').on('click', function (e) {
toggleBtn(this);
});
$('#btnnotifyWeb').on('click', function (e) {
toggleBtn(this);
});
$('#cardBrowseNavFilesystem').on('click', function (e) {
app.goto('Browse','Filesystem');
@ -1100,7 +1114,7 @@ function confirmSettings() {
}
}
if (formOK == true) {
sendAPI({"cmd":"MPD_API_SET_MPD_SETTINGS", "data": {
sendAPI({"cmd":"MPD_API_SET_SETTINGS", "data": {
"consume": ($('#btnconsume').hasClass('btn-success') ? 1 : 0),
"random": ($('#btnrandom').hasClass('btn-success') ? 1 : 0),
"single": ($('#btnsingle').hasClass('btn-success') ? 1 : 0),
@ -1108,11 +1122,9 @@ function confirmSettings() {
"replaygain": $('#selectReplaygain').val(),
"crossfade": $('#inputCrossfade').val(),
"mixrampdb": $('#inputMixrampdb').val(),
"mixrampdelay": $('#inputMixrampdelay').val()
}});
sendAPI({"cmd":"MPD_API_SET_MYMPD_SETTINGS","data": {
"notificationsWeb": ($('#btnnotifyWeb').hasClass('btn-success') ? 1 : 0),
"notificationsPage": ($('#btnnotifyPage').hasClass('btn-success') ? 1 : 0)
"mixrampdelay": $('#inputMixrampdelay').val(),
"notificationWeb": ($('#btnnotifyWeb').hasClass('btn-success') ? 1 : 0),
"notificationPage": ($('#btnnotifyPage').hasClass('btn-success') ? 1 : 0)
}});
$('#settings').modal('hide');
}
@ -1127,33 +1139,7 @@ $('#trashmodebtns > button').on('click', function(e) {
$(this).removeClass("btn-secondary").addClass("btn-success");
});
$('#btnnotifyWeb').on('click', function (e) {
if(Cookies.get('notificationWeb') === 'true') {
Cookies.set('notificationWeb', false, { expires: 424242 });
$('#btnnotifyWeb').removeClass('btn-success').addClass('btn-secondary');
} else {
Notification.requestPermission(function (permission) {
if(!('permission' in Notification)) {
Notification.permission = permission;
}
if (permission === 'granted') {
Cookies.set('notificationWeb', true, { expires: 424242 });
$('#btnnotifyWeb').removeClass('btn-secondary').addClass('btn-success');
}
});
}
});
$('#btnnotifyPage').on('click', function (e) {
if(Cookies.get("notificationPage") === 'true') {
Cookies.set("notificationPage", false, { expires: 424242 });
$('#btnnotifyPage').removeClass('btn-success').addClass('btn-secondary');
} else {
Cookies.set('notificationPage', true, { expires: 424242 });
$('#btnnotifyPage').removeClass('btn-secondary').addClass('btn-success');
}
});
$('#search > input').keypress(function (event) {
if ( event.which == 13 ) {
@ -1221,10 +1207,10 @@ function scrollToTop() {
function gotoPage(x,element,event) {
switch (x) {
case "next":
app.current.page += MAX_ELEMENTS_PER_PAGE;
app.current.page += settings.max_elements_per_page;
break;
case "prev":
app.current.page -= MAX_ELEMENTS_PER_PAGE;
app.current.page -= settings.max_elements_per_page;
if(app.current.page <= 0)
app.current.page = 0;
break;
@ -1251,13 +1237,13 @@ function saveQueue() {
}
function showNotification(notificationTitle,notificationText,notificationHtml,notificationType) {
if (Cookies.get('notificationWeb') === 'true') {
if (settings.notificationWeb == 1) {
var notification = new Notification(notificationTitle, {icon: 'assets/favicon.ico', body: notificationText});
setTimeout(function(notification) {
notification.close();
}, 3000, notification);
}
if (Cookies.get('notificationPage') === 'true') {
if (settings.notificationPage == 1) {
$.notify({ title: notificationTitle, message: notificationHtml},{ type: notificationType, offset: { y: 60, x:20 },
template: '<div data-notify="container" class="col-xs-11 col-sm-3 alert alert-{0}" role="alert">' +
'<button type="button" aria-hidden="true" class="close" data-notify="dismiss">×</button>' +
@ -1285,8 +1271,8 @@ function songChange(obj) {
var coverImg='';
if (obj.data.uri.indexOf('http://') == 0 || obj.data.uri.indexOf('https://') == 0 ) {
coverImg='/assets/coverimage-httpstream.png';
} else if (coverImageFile != '') {
coverImg='/library/'+obj.data.uri.replace(/\/[^\/]+$/,'\/'+coverImageFile);
} else if (settings.coverimage != '') {
coverImg='/library/'+obj.data.uri.replace(/\/[^\/]+$/,'\/'+settings.coverimage);
} else {
coverImg='/assets/coverimage-notavailable.png';
}

View File

@ -29,8 +29,6 @@
</div>
</div>
</main>
<script type="text/javascript" src="js/js.cookie-2.2.0.min.js"></script>
<script type="text/javascript" src="js/player.js"></script>
<script type="text/javascript" src="js/player.js"></script>
</body>
</html>

View File

@ -1,20 +1,19 @@
/*
* Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
* Copyright (c) 2013 Cesanta Software Limited
* Copyright (c) 2018 Cesanta Software Limited
* All rights reserved
*
* This library is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http: *www.gnu.org/licenses/>.
* Licensed under the Apache License, Version 2.0 (the ""License"");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* You are free to use this library under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* Alternatively, you can license this library under a commercial
* license, as set out in <http://cesanta.com/products.html>.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an ""AS IS"" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */
@ -35,6 +34,8 @@
#endif
#ifdef _WIN32
#undef snprintf
#undef vsnprintf
#define snprintf cs_win_snprintf
#define vsnprintf cs_win_vsnprintf
int cs_win_snprintf(char *str, size_t size, const char *format, ...);
@ -47,33 +48,27 @@ typedef unsigned _int64 uint64_t;
#endif
#define PRId64 "I64d"
#define PRIu64 "I64u"
#if !defined(SIZE_T_FMT)
#if _MSC_VER >= 1310
#define SIZE_T_FMT "Iu"
#else
#define SIZE_T_FMT "u"
#endif
#endif
#else /* _WIN32 */
/* <inttypes.h> wants this for C++ */
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
#if !defined(SIZE_T_FMT)
#define SIZE_T_FMT "zu"
#endif
#endif /* _WIN32 */
#ifndef INT64_FMT
#define INT64_FMT PRId64
#endif
#ifndef UINT64_FMT
#define UINT64_FMT PRIu64
#endif
#ifndef va_copy
#define va_copy(x, y) x = y
#endif
#ifndef JSON_MAX_PATH_LEN
#define JSON_MAX_PATH_LEN 60
#define JSON_MAX_PATH_LEN 256
#endif
struct frozen {
@ -85,14 +80,14 @@ struct frozen {
/* For callback API */
char path[JSON_MAX_PATH_LEN];
int path_len;
size_t path_len;
void *callback_data;
json_walk_callback_t callback;
};
struct fstate {
const char *ptr;
int path_len;
size_t path_len;
};
#define SET_STATE(fr, ptr, str, len) \
@ -117,14 +112,15 @@ struct fstate {
static int append_to_path(struct frozen *f, const char *str, int size) {
int n = f->path_len;
f->path_len +=
snprintf(f->path + f->path_len, sizeof(f->path) - (f->path_len + 1),
"%.*s", size, str);
int left = sizeof(f->path) - n - 1;
if (size > left) size = left;
memcpy(f->path + n, str, size);
f->path[n + size] = '\0';
f->path_len += size;
return n;
}
static void truncate_path(struct frozen *f, int len) {
static void truncate_path(struct frozen *f, size_t len) {
f->path_len = len;
f->path[len] = '\0';
}
@ -242,7 +238,7 @@ static int parse_string(struct frozen *f) {
ch = *(unsigned char *) f->cur;
len = get_utf8_char_len((unsigned char) ch);
EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */
EXPECT(len < left(f), JSON_STRING_INCOMPLETE);
EXPECT(len <= left(f), JSON_STRING_INCOMPLETE);
if (ch == '\\') {
EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n);
len += n;
@ -288,9 +284,9 @@ static int parse_number(struct frozen *f) {
static int parse_array(struct frozen *f) {
int i = 0, current_path_len;
char buf[20];
CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0);
TRY(test_and_skip(f, '['));
{
CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0);
{
SET_STATE(f, f->cur - 1, "", 0);
while (cur(f) != ']') {
@ -374,9 +370,6 @@ static int parse_value(struct frozen *f) {
/* key = identifier | string */
static int parse_key(struct frozen *f) {
int ch = cur(f);
#if 0
printf("%s [%.*s]\n", __func__, (int) (f->end - f->cur), f->cur);
#endif
if (is_alpha(ch)) {
TRY(parse_identifier(f));
} else if (ch == '"') {
@ -407,19 +400,17 @@ static int parse_pair(struct frozen *f) {
/* object = '{' pair { ',' pair } '}' */
static int parse_object(struct frozen *f) {
CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0);
TRY(test_and_skip(f, '{'));
{
CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0);
{
SET_STATE(f, f->cur - 1, ".", 1);
while (cur(f) != '}') {
TRY(parse_pair(f));
if (cur(f) == ',') f->cur++;
}
TRY(test_and_skip(f, '}'));
truncate_path(f, fstate.path_len);
CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr);
SET_STATE(f, f->cur - 1, ".", 1);
while (cur(f) != '}') {
TRY(parse_pair(f));
if (cur(f) == ',') f->cur++;
}
TRY(test_and_skip(f, '}'));
truncate_path(f, fstate.path_len);
CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr);
}
return 0;
}
@ -430,7 +421,8 @@ static int doit(struct frozen *f) {
return parse_value(f);
}
static int json_encode_string(struct json_out *out, const char *p, size_t len) {
int json_escape(struct json_out *out, const char *p, size_t len) WEAK;
int json_escape(struct json_out *out, const char *p, size_t len) {
size_t i, cl, n = 0;
const char *hex_digits = "0123456789abcdef";
const char *specials = "btnvfr";
@ -505,7 +497,7 @@ static int b64rev(int c) {
}
}
static uint8_t hexdec(const char *s) {
static unsigned char hexdec(const char *s) {
#define HEXTOI(x) (x >= '0' && x <= '9' ? x - '0' : x - 'W')
int a = tolower(*(const unsigned char *) s);
int b = tolower(*(const unsigned char *) (s + 1));
@ -569,7 +561,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
skip += 2;
} else if (fmt[1] == 'z' && fmt[2] == 'u') {
size_t val = va_arg(ap, size_t);
snprintf(buf, sizeof(buf), "%" SIZE_T_FMT, val);
snprintf(buf, sizeof(buf), "%lu", (unsigned long) val);
len += out->printer(out, buf, strlen(buf));
skip += 1;
} else if (fmt[1] == 'M') {
@ -613,7 +605,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
l = strlen(p);
}
len += out->printer(out, quote, 1);
len += json_encode_string(out, p, l);
len += json_escape(out, p, l);
len += out->printer(out, quote, 1);
}
} else {
@ -628,30 +620,48 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
* TODO(dfrank): reimplement %s and %.*s in order to avoid that.
*/
const char *end_of_format_specifier = "sdfFgGlhuI.*-0123456789";
size_t n = strspn(fmt + 1, end_of_format_specifier);
const char *end_of_format_specifier = "sdfFgGlhuIcx.*-0123456789";
int n = strspn(fmt + 1, end_of_format_specifier);
char *pbuf = buf;
size_t need_len;
int need_len, size = sizeof(buf);
char fmt2[20];
va_list sub_ap;
strncpy(fmt2, fmt, n + 1 > sizeof(fmt2) ? sizeof(fmt2) : n + 1);
va_list ap_copy;
strncpy(fmt2, fmt,
n + 1 > (int) sizeof(fmt2) ? sizeof(fmt2) : (size_t) n + 1);
fmt2[n + 1] = '\0';
va_copy(sub_ap, ap);
need_len =
vsnprintf(buf, sizeof(buf), fmt2, sub_ap) + 1 /* null-term */;
/*
* TODO(lsm): Fix windows & eCos code path here. Their vsnprintf
* implementation returns -1 on overflow rather needed size.
*/
if (need_len > sizeof(buf)) {
va_copy(ap_copy, ap);
need_len = vsnprintf(pbuf, size, fmt2, ap_copy);
va_end(ap_copy);
if (need_len < 0) {
/*
* Windows & eCos vsnprintf implementation return -1 on overflow
* instead of needed size.
*/
pbuf = NULL;
while (need_len < 0) {
free(pbuf);
size *= 2;
if ((pbuf = (char *) malloc(size)) == NULL) break;
va_copy(ap_copy, ap);
need_len = vsnprintf(pbuf, size, fmt2, ap_copy);
va_end(ap_copy);
}
} else if (need_len >= (int) sizeof(buf)) {
/*
* resulting string doesn't fit into a stack-allocated buffer `buf`,
* so we need to allocate a new buffer from heap and use it
*/
pbuf = (char *) malloc(need_len);
va_copy(sub_ap, ap);
vsnprintf(pbuf, need_len, fmt2, sub_ap);
if ((pbuf = (char *) malloc(need_len + 1)) != NULL) {
va_copy(ap_copy, ap);
vsnprintf(pbuf, need_len + 1, fmt2, ap_copy);
va_end(ap_copy);
}
}
if (pbuf == NULL) {
buf[0] = '\0';
pbuf = buf;
}
/*
@ -663,7 +673,6 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
if ((n + 1 == strlen("%" PRId64) && strcmp(fmt2, "%" PRId64) == 0) ||
(n + 1 == strlen("%" PRIu64) && strcmp(fmt2, "%" PRIu64) == 0)) {
(void) va_arg(ap, int64_t);
skip += strlen(PRIu64) - 1;
} else if (strcmp(fmt2, "%.*s") == 0) {
(void) va_arg(ap, int);
(void) va_arg(ap, char *);
@ -704,6 +713,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
}
len += out->printer(out, quote, 1);
} else {
len += out->printer(out, fmt, 1);
fmt++;
}
}
@ -749,7 +759,8 @@ int json_printf_array(struct json_out *out, va_list *ap) {
}
#ifdef _WIN32
int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) WEAK;
int cs_win_vsnprintf(char *str, size_t size, const char *format,
va_list ap) WEAK;
int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
int res = _vsnprintf(str, size, format, ap);
va_end(ap);
@ -788,6 +799,7 @@ int json_walk(const char *json_string, int json_string_length,
}
struct scan_array_info {
int found;
char path[JSON_MAX_PATH_LEN];
struct json_token *token;
};
@ -802,6 +814,7 @@ static void json_scanf_array_elem_cb(void *callback_data, const char *name,
if (strcmp(path, info->path) == 0) {
*info->token = *token;
info->found = 1;
}
}
@ -811,10 +824,11 @@ int json_scanf_array_elem(const char *s, int len, const char *path, int idx,
struct json_token *token) {
struct scan_array_info info;
info.token = token;
info.found = 0;
memset(token, 0, sizeof(*token));
snprintf(info.path, sizeof(info.path), "%s[%d]", path, idx);
json_walk(s, len, json_scanf_array_elem_cb, &info);
return token->len;
return info.found ? token->len : -1;
}
struct json_scanf_info {
@ -856,6 +870,7 @@ static void json_scanf_cb(void *callback_data, const char *name,
size_t name_len, const char *path,
const struct json_token *token) {
struct json_scanf_info *info = (struct json_scanf_info *) callback_data;
char buf[32]; /* Must be enough to hold numbers */
(void) name;
(void) name_len;
@ -876,7 +891,17 @@ static void json_scanf_cb(void *callback_data, const char *name,
switch (info->type) {
case 'B':
info->num_conversions++;
*(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0);
switch (sizeof(bool)) {
case sizeof(char):
*(char *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0);
break;
case sizeof(int):
*(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0);
break;
default:
/* should never be here */
abort();
}
break;
case 'M': {
union {
@ -889,12 +914,21 @@ static void json_scanf_cb(void *callback_data, const char *name,
}
case 'Q': {
char **dst = (char **) info->target;
int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0);
if (unescaped_len >= 0 &&
(*dst = (char *) malloc(unescaped_len + 1)) != NULL) {
info->num_conversions++;
json_unescape(token->ptr, token->len, *dst, unescaped_len);
(*dst)[unescaped_len] = '\0';
if (token->type == JSON_TYPE_NULL) {
*dst = NULL;
} else {
int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0);
if (unescaped_len >= 0 &&
(*dst = (char *) malloc(unescaped_len + 1)) != NULL) {
info->num_conversions++;
if (json_unescape(token->ptr, token->len, *dst, unescaped_len) ==
unescaped_len) {
(*dst)[unescaped_len] = '\0';
} else {
free(*dst);
*dst = NULL;
}
}
}
break;
}
@ -927,7 +961,12 @@ static void json_scanf_cb(void *callback_data, const char *name,
*(struct json_token *) info->target = *token;
break;
default:
info->num_conversions += sscanf(token->ptr, info->fmt, info->target);
/* Before scanf, copy into tmp buffer in order to 0-terminate it */
if (token->len < (int) sizeof(buf)) {
memcpy(buf, token->ptr, token->len);
buf[token->len] = '\0';
info->num_conversions += sscanf(buf, info->fmt, info->target);
}
break;
}
}
@ -992,3 +1031,380 @@ int json_scanf(const char *str, int len, const char *fmt, ...) {
va_end(ap);
return result;
}
int json_vfprintf(const char *file_name, const char *fmt, va_list ap) WEAK;
int json_vfprintf(const char *file_name, const char *fmt, va_list ap) {
int res = -1;
FILE *fp = fopen(file_name, "wb");
if (fp != NULL) {
struct json_out out = JSON_OUT_FILE(fp);
res = json_vprintf(&out, fmt, ap);
fputc('\n', fp);
fclose(fp);
}
return res;
}
int json_fprintf(const char *file_name, const char *fmt, ...) WEAK;
int json_fprintf(const char *file_name, const char *fmt, ...) {
int result;
va_list ap;
va_start(ap, fmt);
result = json_vfprintf(file_name, fmt, ap);
va_end(ap);
return result;
}
char *json_fread(const char *path) WEAK;
char *json_fread(const char *path) {
FILE *fp;
char *data = NULL;
if ((fp = fopen(path, "rb")) == NULL) {
} else if (fseek(fp, 0, SEEK_END) != 0) {
fclose(fp);
} else {
long size = ftell(fp);
if (size > 0 && (data = (char *) malloc(size + 1)) != NULL) {
fseek(fp, 0, SEEK_SET); /* Some platforms might not have rewind(), Oo */
if (fread(data, 1, size, fp) != (size_t) size) {
free(data);
data = NULL;
} else {
data[size] = '\0';
}
}
fclose(fp);
}
return data;
}
struct json_setf_data {
const char *json_path;
const char *base; /* Pointer to the source JSON string */
int matched; /* Matched part of json_path */
int pos; /* Offset of the mutated value begin */
int end; /* Offset of the mutated value end */
int prev; /* Offset of the previous token end */
};
static int get_matched_prefix_len(const char *s1, const char *s2) {
int i = 0;
while (s1[i] && s2[i] && s1[i] == s2[i]) i++;
return i;
}
static void json_vsetf_cb(void *userdata, const char *name, size_t name_len,
const char *path, const struct json_token *t) {
struct json_setf_data *data = (struct json_setf_data *) userdata;
int off, len = get_matched_prefix_len(path, data->json_path);
if (t->ptr == NULL) return;
off = t->ptr - data->base;
// printf("--%d %s %d\n", t->type, path, off);
if (len > data->matched) data->matched = len;
/*
* If there is no exact path match, set the mutation position to tbe end
* of the object or array
*/
if (len < data->matched && data->pos == 0 &&
(t->type == JSON_TYPE_OBJECT_END || t->type == JSON_TYPE_ARRAY_END)) {
data->pos = data->end = data->prev;
}
/* Exact path match. Set mutation position to the value of this token */
if (strcmp(path, data->json_path) == 0 && t->type != JSON_TYPE_OBJECT_START &&
t->type != JSON_TYPE_ARRAY_START) {
data->pos = off;
data->end = off + t->len;
}
/*
* For deletion, we need to know where the previous value ends, because
* we don't know where matched value key starts.
* When the mutation position is not yet set, remember each value end.
* When the mutation position is already set, but it is at the beginning
* of the object/array, we catch the end of the object/array and see
* whether the object/array start is closer then previously stored prev.
*/
if (data->pos == 0) {
data->prev = off + t->len; /* pos is not yet set */
} else if ((t->ptr[0] == '[' || t->ptr[0] == '{') && off + 1 < data->pos &&
off + 1 > data->prev) {
data->prev = off + 1;
}
(void) name;
(void) name_len;
}
int json_vsetf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, va_list ap) WEAK;
int json_vsetf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, va_list ap) {
struct json_setf_data data;
memset(&data, 0, sizeof(data));
data.json_path = json_path;
data.base = s;
data.end = len;
// printf("S:[%.*s] %s %p\n", len, s, json_path, json_fmt);
json_walk(s, len, json_vsetf_cb, &data);
// printf("-> %d %d %d\n", data.prev, data.pos, data.end);
if (json_fmt == NULL) {
/* Deletion codepath */
json_printf(out, "%.*s", data.prev, s);
/* Trim comma after the value that begins at object/array start */
if (s[data.prev - 1] == '{' || s[data.prev - 1] == '[') {
int i = data.end;
while (i < len && is_space(s[i])) i++;
if (s[i] == ',') data.end = i + 1; /* Point after comma */
}
json_printf(out, "%.*s", len - data.end, s + data.end);
} else {
/* Modification codepath */
int n, off = data.matched, depth = 0;
/* Print the unchanged beginning */
json_printf(out, "%.*s", data.pos, s);
/* Add missing keys */
while ((n = strcspn(&json_path[off], ".[")) > 0) {
if (s[data.prev - 1] != '{' && s[data.prev - 1] != '[' && depth == 0) {
json_printf(out, ",");
}
if (off > 0 && json_path[off - 1] != '.') break;
json_printf(out, "%.*Q:", 1, json_path + off);
off += n;
if (json_path[off] != '\0') {
json_printf(out, "%c", json_path[off] == '.' ? '{' : '[');
depth++;
off++;
}
}
/* Print the new value */
json_vprintf(out, json_fmt, ap);
/* Close brackets/braces of the added missing keys */
for (; off > data.matched; off--) {
int ch = json_path[off];
const char *p = ch == '.' ? "}" : ch == '[' ? "]" : "";
json_printf(out, "%s", p);
}
/* Print the rest of the unchanged string */
json_printf(out, "%.*s", len - data.end, s + data.end);
}
return data.end > data.pos ? 1 : 0;
}
int json_setf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, ...) WEAK;
int json_setf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, ...) {
int result;
va_list ap;
va_start(ap, json_fmt);
result = json_vsetf(s, len, out, json_path, json_fmt, ap);
va_end(ap);
return result;
}
struct prettify_data {
struct json_out *out;
int level;
int last_token;
};
static void indent(struct json_out *out, int level) {
while (level-- > 0) out->printer(out, " ", 2);
}
static void print_key(struct prettify_data *pd, const char *path,
const char *name, int name_len) {
if (pd->last_token != JSON_TYPE_INVALID &&
pd->last_token != JSON_TYPE_ARRAY_START &&
pd->last_token != JSON_TYPE_OBJECT_START) {
pd->out->printer(pd->out, ",", 1);
}
if (path[0] != '\0') pd->out->printer(pd->out, "\n", 1);
indent(pd->out, pd->level);
if (path[0] != '\0' && path[strlen(path) - 1] != ']') {
pd->out->printer(pd->out, "\"", 1);
pd->out->printer(pd->out, name, (int) name_len);
pd->out->printer(pd->out, "\"", 1);
pd->out->printer(pd->out, ": ", 2);
}
}
static void prettify_cb(void *userdata, const char *name, size_t name_len,
const char *path, const struct json_token *t) {
struct prettify_data *pd = (struct prettify_data *) userdata;
switch (t->type) {
case JSON_TYPE_OBJECT_START:
case JSON_TYPE_ARRAY_START:
print_key(pd, path, name, name_len);
pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_START ? "[" : "{",
1);
pd->level++;
break;
case JSON_TYPE_OBJECT_END:
case JSON_TYPE_ARRAY_END:
pd->level--;
if (pd->last_token != JSON_TYPE_INVALID &&
pd->last_token != JSON_TYPE_ARRAY_START &&
pd->last_token != JSON_TYPE_OBJECT_START) {
pd->out->printer(pd->out, "\n", 1);
indent(pd->out, pd->level);
}
pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_END ? "]" : "}", 1);
break;
case JSON_TYPE_NUMBER:
case JSON_TYPE_NULL:
case JSON_TYPE_TRUE:
case JSON_TYPE_FALSE:
case JSON_TYPE_STRING:
print_key(pd, path, name, name_len);
if (t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, "\"", 1);
pd->out->printer(pd->out, t->ptr, t->len);
if (t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, "\"", 1);
break;
default:
break;
}
pd->last_token = t->type;
}
int json_prettify(const char *s, int len, struct json_out *out) WEAK;
int json_prettify(const char *s, int len, struct json_out *out) {
struct prettify_data pd = {out, 0, JSON_TYPE_INVALID};
return json_walk(s, len, prettify_cb, &pd);
}
int json_prettify_file(const char *file_name) WEAK;
int json_prettify_file(const char *file_name) {
int res = -1;
char *s = json_fread(file_name);
FILE *fp;
if (s != NULL && (fp = fopen(file_name, "wb")) != NULL) {
struct json_out out = JSON_OUT_FILE(fp);
res = json_prettify(s, strlen(s), &out);
if (res < 0) {
/* On error, restore the old content */
fclose(fp);
fp = fopen(file_name, "wb");
fseek(fp, 0, SEEK_SET);
fwrite(s, 1, strlen(s), fp);
} else {
fputc('\n', fp);
}
fclose(fp);
}
free(s);
return res;
}
struct next_data {
void *handle; // Passed handle. Changed if a next entry is found
const char *path; // Path to the iterated object/array
int path_len; // Path length - optimisation
int found; // Non-0 if found the next entry
struct json_token *key; // Object's key
struct json_token *val; // Object's value
int *idx; // Array index
};
static void next_set_key(struct next_data *d, const char *name, int name_len,
int is_array) {
if (is_array) {
/* Array. Set index and reset key */
if (d->key != NULL) {
d->key->len = 0;
d->key->ptr = NULL;
}
if (d->idx != NULL) *d->idx = atoi(name);
} else {
/* Object. Set key and make index -1 */
if (d->key != NULL) {
d->key->ptr = name;
d->key->len = name_len;
}
if (d->idx != NULL) *d->idx = -1;
}
}
static void next_cb(void *userdata, const char *name, size_t name_len,
const char *path, const struct json_token *t) {
struct next_data *d = (struct next_data *) userdata;
const char *p = path + d->path_len;
if (d->found) return;
if (d->path_len >= (int) strlen(path)) return;
if (strncmp(d->path, path, d->path_len) != 0) return;
if (strchr(p + 1, '.') != NULL) return; /* More nested objects - skip */
if (strchr(p + 1, '[') != NULL) return; /* Ditto for arrays */
// {OBJECT,ARRAY}_END types do not pass name, _START does. Save key.
if (t->type == JSON_TYPE_OBJECT_START || t->type == JSON_TYPE_ARRAY_START) {
// printf("SAV %s %d %p\n", path, t->type, t->ptr);
next_set_key(d, name, name_len, p[0] == '[');
} else if (d->handle == NULL || d->handle < (void *) t->ptr) {
// printf("END %s %d %p\n", path, t->type, t->ptr);
if (t->type != JSON_TYPE_OBJECT_END && t->type != JSON_TYPE_ARRAY_END) {
next_set_key(d, name, name_len, p[0] == '[');
}
if (d->val != NULL) *d->val = *t;
d->handle = (void *) t->ptr;
d->found = 1;
}
}
static void *json_next(const char *s, int len, void *handle, const char *path,
struct json_token *key, struct json_token *val, int *i) {
struct json_token tmpval, *v = val == NULL ? &tmpval : val;
struct json_token tmpkey, *k = key == NULL ? &tmpkey : key;
int tmpidx, *pidx = i == NULL ? &tmpidx : i;
struct next_data data = {handle, path, strlen(path), 0, k, v, pidx};
json_walk(s, len, next_cb, &data);
return data.found ? data.handle : NULL;
}
void *json_next_key(const char *s, int len, void *handle, const char *path,
struct json_token *key, struct json_token *val) WEAK;
void *json_next_key(const char *s, int len, void *handle, const char *path,
struct json_token *key, struct json_token *val) {
return json_next(s, len, handle, path, key, val, NULL);
}
void *json_next_elem(const char *s, int len, void *handle, const char *path,
int *idx, struct json_token *val) WEAK;
void *json_next_elem(const char *s, int len, void *handle, const char *path,
int *idx, struct json_token *val) {
return json_next(s, len, handle, path, NULL, val, idx);
}
static int json_sprinter(struct json_out *out, const char *str, size_t len) {
size_t old_len = out->u.buf.buf == NULL ? 0 : strlen(out->u.buf.buf);
size_t new_len = len + old_len;
char *p = (char *) realloc(out->u.buf.buf, new_len + 1);
if (p != NULL) {
memcpy(p + old_len, str, len);
p[new_len] = '\0';
out->u.buf.buf = p;
}
return len;
}
char *json_vasprintf(const char *fmt, va_list ap) WEAK;
char *json_vasprintf(const char *fmt, va_list ap) {
struct json_out out;
memset(&out, 0, sizeof(out));
out.printer = json_sprinter;
json_vprintf(&out, fmt, ap);
return out.u.buf.buf;
}
char *json_asprintf(const char *fmt, ...) WEAK;
char *json_asprintf(const char *fmt, ...) {
char *result = NULL;
va_list ap;
va_start(ap, fmt);
result = json_vasprintf(fmt, ap);
va_end(ap);
return result;
}

View File

@ -1,20 +1,19 @@
/*
* Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
* Copyright (c) 2013 Cesanta Software Limited
* Copyright (c) 2018 Cesanta Software Limited
* All rights reserved
*
* This library is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http: *www.gnu.org/licenses/>.
* Licensed under the Apache License, Version 2.0 (the ""License"");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* You are free to use this library under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* Alternatively, you can license this library under a commercial
* license, as set out in <http://cesanta.com/products.html>.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an ""AS IS"" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CS_FROZEN_FROZEN_H_
@ -28,6 +27,13 @@ extern "C" {
#include <stddef.h>
#include <stdio.h>
#if defined(_WIN32) && _MSC_VER < 1700
typedef int bool;
enum { false = 0, true = 1 };
#else
#include <stdbool.h>
#endif
/* JSON token type */
enum json_token_type {
JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */
@ -94,6 +100,7 @@ typedef void (*json_walk_callback_t)(void *callback_data, const char *name,
/*
* Parse `json_string`, invoking `callback` in a way similar to SAX parsers;
* see `json_walk_callback_t`.
* Return number of processed bytes, or a negative error code.
*/
int json_walk(const char *json_string, int json_string_length,
json_walk_callback_t callback, void *callback_data);
@ -127,7 +134,7 @@ extern int json_printer_file(struct json_out *, const char *, size_t);
#define JSON_OUT_FILE(fp) \
{ \
json_printer_file, { \
{ (void *) fp, 0, 0 } \
{ (char *) fp, 0, 0 } \
} \
}
@ -144,13 +151,34 @@ typedef int (*json_printf_callback_t)(struct json_out *, va_list *ap);
* - `%M` invokes a json_printf_callback_t function. That callback function
* can consume more parameters.
*
* Return number of bytes printed. If the return value is bigger then the
* Return number of bytes printed. If the return value is bigger than the
* supplied buffer, that is an indicator of overflow. In the overflow case,
* overflown bytes are not printed.
*/
int json_printf(struct json_out *, const char *fmt, ...);
int json_vprintf(struct json_out *, const char *fmt, va_list ap);
/*
* Same as json_printf, but prints to a file.
* File is created if does not exist. File is truncated if already exists.
*/
int json_fprintf(const char *file_name, const char *fmt, ...);
int json_vfprintf(const char *file_name, const char *fmt, va_list ap);
/*
* Print JSON into an allocated 0-terminated string.
* Return allocated string, or NULL on error.
* Example:
*
* ```c
* char *str = json_asprintf("{a:%H}", 3, "abc");
* printf("%s\n", str); // Prints "616263"
* free(str);
* ```
*/
char *json_asprintf(const char *fmt, ...);
char *json_vasprintf(const char *fmt, va_list ap);
/*
* Helper %M callback that prints contiguous C arrays.
* Consumes void *array_ptr, size_t array_size, size_t elem_size, char *fmt
@ -165,7 +193,8 @@ int json_printf_array(struct json_out *, va_list *ap);
* 1. Object keys in the format string may be not quoted, e.g. "{key: %d}"
* 2. Order of keys in an object is irrelevant.
* 3. Several extra format specifiers are supported:
* - %B: consumes `int *`, expects boolean `true` or `false`.
* - %B: consumes `int *` (or `char *`, if `sizeof(bool) == sizeof(char)`),
* expects boolean `true` or `false`.
* - %Q: consumes `char **`, expects quoted, JSON-encoded string. Scanned
* string is malloc-ed, caller must free() the string.
* - %V: consumes `char **`, `int *`. Expects base64-encoded string.
@ -193,7 +222,7 @@ typedef void (*json_scanner_t)(const char *str, int len, void *user_data);
/*
* Helper function to scan array item with given path and index.
* Fills `token` with the matched JSON token.
* Return 0 if no array element found, otherwise non-0.
* Return -1 if no array element found, otherwise non-negative token length.
*/
int json_scanf_array_elem(const char *s, int len, const char *path, int index,
struct json_token *token);
@ -207,6 +236,76 @@ int json_scanf_array_elem(const char *s, int len, const char *path, int index,
*/
int json_unescape(const char *src, int slen, char *dst, int dlen);
/*
* Escape a string `str`, `str_len` into the printer `out`.
* Return the number of bytes printed.
*/
int json_escape(struct json_out *out, const char *str, size_t str_len);
/*
* Read the whole file in memory.
* Return malloc-ed file content, or NULL on error. The caller must free().
*/
char *json_fread(const char *file_name);
/*
* Update given JSON string `s,len` by changing the value at given `json_path`.
* The result is saved to `out`. If `json_fmt` == NULL, that deletes the key.
* If path is not present, missing keys are added. Array path without an
* index pushes a value to the end of an array.
* Return 1 if the string was changed, 0 otherwise.
*
* Example: s is a JSON string { "a": 1, "b": [ 2 ] }
* json_setf(s, len, out, ".a", "7"); // { "a": 7, "b": [ 2 ] }
* json_setf(s, len, out, ".b", "7"); // { "a": 1, "b": 7 }
* json_setf(s, len, out, ".b[]", "7"); // { "a": 1, "b": [ 2,7 ] }
* json_setf(s, len, out, ".b", NULL); // { "a": 1 }
*/
int json_setf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, ...);
int json_vsetf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, va_list ap);
/*
* Pretty-print JSON string `s,len` into `out`.
* Return number of processed bytes in `s`.
*/
int json_prettify(const char *s, int len, struct json_out *out);
/*
* Prettify JSON file `file_name`.
* Return number of processed bytes, or negative number of error.
* On error, file content is not modified.
*/
int json_prettify_file(const char *file_name);
/*
* Iterate over an object at given JSON `path`.
* On each iteration, fill the `key` and `val` tokens. It is OK to pass NULL
* for `key`, or `val`, in which case they won't be populated.
* Return an opaque value suitable for the next iteration, or NULL when done.
*
* Example:
*
* ```c
* void *h = NULL;
* struct json_token key, val;
* while ((h = json_next_key(s, len, h, ".foo", &key, &val)) != NULL) {
* printf("[%.*s] -> [%.*s]\n", key.len, key.ptr, val.len, val.ptr);
* }
* ```
*/
void *json_next_key(const char *s, int len, void *handle, const char *path,
struct json_token *key, struct json_token *val);
/*
* Iterate over an array at given JSON `path`.
* Similar to `json_next_key`, but fills array index `idx` instead of `key`.
*/
void *json_next_elem(const char *s, int len, void *handle, const char *path,
int *idx, struct json_token *val);
#ifdef __cplusplus
}
#endif /* __cplusplus */

View File

@ -59,8 +59,8 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
int je, int_buf;
float float_buf;
char *p_charbuf1, *p_charbuf2;
FILE *fp;
struct mympd_state { int a; int b; } state = { .a = 0, .b = 0 };
#ifdef DEBUG
fprintf(stdout,"Got request: %s\n",msg.p);
#endif
@ -72,24 +72,10 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
return;
switch(cmd_id) {
case MPD_API_SET_MYMPD_SETTINGS:
fp = fopen(mpd.statefile,"w");
if(fp != NULL) {
fprintf(fp,"%.*s",msg.len,msg.p);
fclose(fp);
} else {
fprintf(stderr,"Cant write state file\n");
}
break;
case MPD_API_GET_MYMPD_SETTINGS:
fp = fopen(mpd.statefile,"r");
if(fp != NULL) {
fgets(mpd.buf, MAX_SIZE, fp);
fclose(fp);
n=strlen(mpd.buf);
}
break;
case MPD_API_SET_MPD_SETTINGS:
case MPD_API_SET_SETTINGS:
json_scanf(msg.p, msg.len, "{ data: { notificationWeb: %d, notificationPage: %d} }", &state.a, &state.b);
json_fprintf(mpd.statefile, "{ notificationWeb: %d, notificationPage: %d}", state.a, state.b);
je = json_scanf(msg.p, msg.len, "{ data: { random:%u } }", &uint_buf1);
if (je == 1)
mpd_run_random(mpd.conn, uint_buf1);
@ -305,7 +291,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
mpd_run_rm(mpd.conn, p_charbuf1);
free(p_charbuf1);
break;
case MPD_API_GET_MPD_SETTINGS:
case MPD_API_GET_SETTINGS:
n = mympd_put_settings(mpd.buf);
break;
case MPD_API_GET_STATS:
@ -630,6 +616,14 @@ int mympd_put_settings(char *buffer)
char *replaygain;
int len;
struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE);
struct mympd_state { int a; int b; } state = { .a = 0, .b = 0 };
if( access( mpd.statefile, F_OK ) != -1 ) {
char *content = json_fread(mpd.statefile);
json_scanf(content, strlen(content), "{notificationWeb: %d, notificationPage: %d}", &state.a, &state.b);
} else {
state.a=0;
state.b=0;
}
status = mpd_run_status(mpd.conn);
if (!status) {
@ -644,12 +638,13 @@ int mympd_put_settings(char *buffer)
replaygain=strdup(pair->value);
mpd_return_pair(mpd.conn, pair);
}
len = json_printf(&out,
"{type:settings, data:{"
"repeat:%d, single:%d, crossfade:%d, consume:%d, random:%d, "
"mixrampdb: %f, mixrampdelay: %f, mpdhost : %Q, mpdport: %d, passwort_set: %B, "
"streamport: %d, coverimage: %Q, max_elements_per_page: %d, replaygain: %Q"
"streamport: %d, coverimage: %Q, max_elements_per_page: %d, replaygain: %Q,"
"notificationWeb: %d, notificationPage: %d"
"}}",
mpd_status_get_repeat(status),
mpd_status_get_single(status),
@ -662,7 +657,9 @@ int mympd_put_settings(char *buffer)
mpd.password ? "true" : "false",
streamport, coverimage,
MAX_ELEMENTS_PER_PAGE,
replaygain
replaygain,
state.a,
state.b
);
mpd_status_free(status);

View File

@ -78,10 +78,8 @@
X(MPD_API_GET_ARTISTS) \
X(MPD_API_GET_CURRENT_SONG) \
X(MPD_API_WELCOME) \
X(MPD_API_GET_MPD_SETTINGS) \
X(MPD_API_SET_MPD_SETTINGS) \
X(MPD_API_SET_MYMPD_SETTINGS) \
X(MPD_API_GET_MYMPD_SETTINGS)
X(MPD_API_GET_SETTINGS) \
X(MPD_API_SET_SETTINGS)
enum mpd_cmd_ids {
MPD_CMDS(GEN_ENUM)