Assists music production by grouping standalone programs into sessions. Community version of "Non Session Manager".
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

481 lines
9.4KB

  1. /*******************************************************************************/
  2. /* Copyright (C) 2008 Jonathan Moore Liles */
  3. /* */
  4. /* This program is free software; you can redistribute it and/or modify it */
  5. /* under the terms of the GNU General Public License as published by the */
  6. /* Free Software Foundation; either version 2 of the License, or (at your */
  7. /* option) any later version. */
  8. /* */
  9. /* This program is distributed in the hope that it will be useful, but WITHOUT */
  10. /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
  11. /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
  12. /* more details. */
  13. /* */
  14. /* You should have received a copy of the GNU General Public License along */
  15. /* with This program; see the file COPYING. If not,write to the Free Software */
  16. /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  17. /*******************************************************************************/
  18. #define _LOGGABLE_C
  19. #include "Loggable.H"
  20. #undef _LOGABLE_C
  21. #include <stdlib.h>
  22. #include <stdio.h>
  23. #include <stdarg.h>
  24. #include <string.h>
  25. FILE *Loggable::_fp;
  26. int Loggable::_log_id = 0;
  27. int Loggable::_level = 0;
  28. int Loggable::_undo_index = 1;
  29. vector <Loggable *> Loggable::_loggables;
  30. map <string, create_func*> Loggable::_class_map;
  31. queue <char *> Loggable::_transaction;
  32. bool
  33. Loggable::open ( const char *filename )
  34. {
  35. if ( ! ( Loggable::_fp = fopen( filename, "a+" ) ) )
  36. {
  37. printf( "Could not open log file for writing!" );
  38. return false;
  39. }
  40. return true;
  41. }
  42. /** sigh. parse a string of ":name value :name value" pairs into an
  43. * array of strings, one per pair */
  44. // FIXME: doesn't handle the case of :name ":foo bar", nested quotes
  45. // or other things it should. Also, quotes should be removed here, not
  46. // in client code.
  47. static
  48. char **
  49. parse_alist( const char *s )
  50. {
  51. // FIXME: bogus over allocation...
  52. int tl = strlen( s );
  53. char **r = (char**)malloc( sizeof( char* ) * tl );
  54. const char *e = s + tl;
  55. const char *c = NULL;
  56. int i = 0;
  57. for ( ; ; s++ )
  58. {
  59. /* if ( *s == '\n' ) */
  60. /* break; */
  61. // if ( *s == ':' || s == e )
  62. if ( *s == ':' || *s == '\0' )
  63. {
  64. if ( c )
  65. {
  66. int l = s - c;
  67. char *pair = (char*)malloc( l + 1 );
  68. strncpy( pair, c, l );
  69. pair[ l ] = '\0';
  70. r[ i++ ] = pair;
  71. }
  72. c = s;
  73. if ( *s == '\0' )
  74. break;
  75. }
  76. }
  77. r[ i ] = NULL;
  78. return r;
  79. }
  80. static
  81. void free_sa ( char **sa )
  82. {
  83. char **a = sa;
  84. for ( ; *a; a++ )
  85. free( *a );
  86. free( sa );
  87. }
  88. /** 'do' a message like "Region 0xF1 set :r 123" */
  89. bool
  90. Loggable::do_this ( const char *s, bool reverse )
  91. {
  92. int id;
  93. sscanf( s, "%*s %X ", &id );
  94. Loggable *l = find( id );
  95. // assert( l );
  96. char classname[40];
  97. char command[40];
  98. char *arguments;
  99. const char *create, *destroy;
  100. if ( reverse )
  101. {
  102. sscanf( s, "%s %*X %s %*[^\n<] %a[^\n]", classname, command, &arguments );
  103. create = "destroy";
  104. destroy = "create";
  105. }
  106. else
  107. {
  108. sscanf( s, "%s %*X %s %a[^\n<]", classname, command, &arguments );
  109. create = "create";
  110. destroy = "destroy";
  111. }
  112. if ( ! strcmp( command, destroy ) )
  113. {
  114. int id = l->id();
  115. delete l;
  116. _loggables[ id ] = NULL;
  117. }
  118. else if ( ! strcmp( command, "set" ) )
  119. {
  120. printf( "got set command.\n" );
  121. char **sa = parse_alist( arguments );
  122. l->log_start();
  123. l->set( sa );
  124. l->log_end();
  125. }
  126. else if ( ! strcmp( command, create ) )
  127. {
  128. char **sa = parse_alist( arguments );
  129. if ( ! _class_map[ string( classname ) ] )
  130. printf( "error class %s is unregistered!\n", classname );
  131. else
  132. {
  133. /* create */
  134. Loggable *l = _class_map[ string( classname ) ]( sa );
  135. l->update_id( id );
  136. l->log_create();
  137. }
  138. }
  139. }
  140. void
  141. Loggable::undo ( void )
  142. {
  143. char *buf = new char[ BUFSIZ ];
  144. // fflush( _fp );
  145. fseek( _fp, 0, SEEK_END );
  146. size_t len = ftell( _fp );
  147. fseek( _fp, 0 - (BUFSIZ > len ? len : BUFSIZ), SEEK_END );
  148. len = fread( buf, 1, BUFSIZ, _fp );
  149. char *s = buf + len - 1;
  150. // FIXME: handle blocks
  151. int i = 1;
  152. /* move back _undo_index items from the end */
  153. for ( int j = _undo_index; j-- ; )
  154. for ( --s; *s && s >= buf; --s, ++i )
  155. {
  156. if ( *s == '\n' )
  157. {
  158. if ( *(s + 1) == '\t' )
  159. continue;
  160. if ( *(s + 1) == '}' )
  161. {
  162. *(s + 1) = NULL;
  163. continue;
  164. }
  165. break;
  166. }
  167. }
  168. s++;
  169. buf[ len ] = NULL;
  170. if ( ! strlen( s ) )
  171. {
  172. printf( "corrupt undo file or no undo entries.\n" );
  173. return;
  174. }
  175. char *b = s;
  176. s += strlen( s ) - 1;
  177. if ( strtok( b, "\n" ) == NULL )
  178. {
  179. printf( "error, empty undo transaction!\n" );
  180. abort();
  181. }
  182. int n = 1;
  183. while ( strtok( NULL, "\n" ) )
  184. ++n;
  185. int ui = _undo_index;
  186. block_start();
  187. if ( strcmp( b, "{" ) )
  188. {
  189. /* It's a single undo, get rid of trailing messages in this block */
  190. n = 1;
  191. s = b + 2;
  192. s += strlen( s ) - 1;
  193. }
  194. while ( n-- )
  195. {
  196. while ( s >= b && *(--s) );
  197. s++;
  198. if ( ! strcmp( s, "{" ) )
  199. break;
  200. if ( *s == '\t' )
  201. s++;
  202. printf( "undoing \"%s\"\n", s );
  203. do_this( s, true );
  204. s -= 2;
  205. }
  206. block_end();
  207. // FIXME: bogus... needs to account for multiple events.
  208. _undo_index = ui + 1;
  209. ++_undo_index;
  210. delete buf;
  211. }
  212. /** write a snapshot of the state of all loggable objects, sufficient
  213. * for later reconstruction, to /file/ */
  214. bool
  215. Loggable::snapshot( const char *file )
  216. {
  217. FILE *ofp = _fp;
  218. if ( ! ( _fp = fopen( file, "a" ) ) )
  219. {
  220. _fp = ofp;
  221. return false;
  222. }
  223. for ( int i = 0; i < _log_id; ++i )
  224. {
  225. _loggables[ i ]->log_create();
  226. }
  227. fclose( _fp );
  228. _fp = ofp;
  229. return true;
  230. }
  231. void
  232. Loggable::log ( const char *fmt, ... )
  233. {
  234. /* FIXME: bogus limit */
  235. static char buf[1024];
  236. static int i = 0;
  237. va_list args;
  238. if ( fmt )
  239. {
  240. va_start( args, fmt );
  241. i += vsprintf( buf + i, fmt, args );
  242. va_end( args );
  243. }
  244. if ( rindex( buf, '\n' ) )
  245. {
  246. _transaction.push( strdup( buf ) );
  247. i = 0;
  248. }
  249. }
  250. void
  251. Loggable::flush ( void )
  252. {
  253. int n = _transaction.size();
  254. if ( n > 1 )
  255. fprintf( _fp, "{\n" );
  256. while ( ! _transaction.empty() )
  257. {
  258. char *s = _transaction.front();
  259. _transaction.pop();
  260. if ( n > 1 )
  261. fprintf( _fp, "\t" );
  262. fprintf( _fp, "%s", s );
  263. free( s );
  264. }
  265. if ( n > 1 )
  266. fprintf( _fp, "}\n" );
  267. fflush( _fp );
  268. }
  269. void
  270. Loggable::log_print( char **o, char **n )
  271. {
  272. if ( n )
  273. for ( ; *n; n++ )
  274. log( "%s%s", *n, *(n + 1) ? " " : "" );
  275. if ( o && *o )
  276. {
  277. if ( n ) log( " << " );
  278. for ( ; *o; o++ )
  279. log( "%s%s", *o, *(o + 1) ? " " : "" );
  280. }
  281. log( "\n" );
  282. }
  283. /** compare elements of dumps s1 and s2, removing those elements
  284. of dst which are not changed from src */
  285. static
  286. bool
  287. log_diff ( char **sa1, char **sa2 )
  288. {
  289. if ( ! sa1 )
  290. return true;
  291. int w = 0;
  292. for ( int i = 0; sa1[ i ]; ++i )
  293. {
  294. if ( ! strcmp( sa1[ i ], sa2[ i ] ) )
  295. {
  296. free( sa2[ i ] );
  297. free( sa1[ i ] );
  298. }
  299. else
  300. {
  301. sa2[ w ] = sa2[ i ];
  302. sa1[ w ] = sa1[ i ];
  303. w++;
  304. }
  305. }
  306. sa1[ w ] = NULL;
  307. sa2[ w ] = NULL;
  308. return w == 0 ? false : true;
  309. }
  310. void
  311. Loggable::log_start ( void )
  312. {
  313. if ( ! _old_state )
  314. _old_state = get();
  315. ++_nest;
  316. _undo_index = 1;
  317. }
  318. void
  319. Loggable::log_end ( void )
  320. {
  321. if ( --_nest > 0 )
  322. return;
  323. char **_new_state = get();
  324. if ( log_diff( _old_state, _new_state ) )
  325. {
  326. // indent();
  327. log( "%s 0x%X set ", class_name(), _id );
  328. log_print( _old_state, _new_state );
  329. }
  330. if ( _new_state )
  331. free_sa( _new_state );
  332. if ( _old_state )
  333. free_sa( _old_state );
  334. _old_state = NULL;
  335. if ( Loggable::_level == 0 )
  336. Loggable::flush();
  337. }
  338. void
  339. Loggable::log_create ( void )
  340. {
  341. // indent();
  342. log( "%s 0x%X create ", class_name(), _id );
  343. char **sa = get();
  344. if ( sa )
  345. {
  346. log_print( NULL, sa );
  347. free_sa( sa );
  348. }
  349. else
  350. log( "\n" );
  351. if ( Loggable::_level == 0 )
  352. Loggable::flush();
  353. }
  354. void
  355. Loggable::log_destroy ( void )
  356. {
  357. // indent();
  358. log( "%s 0x%X destroy (nothing) << ", class_name(), _id );
  359. char **sa = get();
  360. // log_print( sa, NULL );
  361. log_print( NULL, sa );
  362. free_sa( sa );
  363. if ( Loggable::_level == 0 )
  364. Loggable::flush();
  365. }