lastFripper.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. /*
  2. released as a public domain
  3. std.denis, 2009
  4. */
  5. #include "../../structures.h"
  6. #include "lastFripper.h"
  7. #define strdup _strdup
  8. #define strnicmp _strnicmp
  9. #define ltoa _ltoa
  10. #define mkdir _mkdir
  11. #define PART_SUFFIX "_partial"
  12. void my_mkdir( char* name )
  13. {
  14. char* pdir = name;
  15. while( 1 ) {
  16. char ch;
  17. char* pnext = pdir;
  18. while( *pnext && *pnext != '\\' && *pnext != '/' )
  19. pnext++;
  20. if( *pnext == 0 )
  21. break;
  22. ch = *pnext;
  23. *pnext = 0;
  24. mkdir( name );
  25. *pnext = ch;
  26. pdir = pnext + 1;
  27. }
  28. mkdir( name );
  29. }
  30. __inline void myOutputDebugStringA1( char* str )
  31. {
  32. /* char fname[128];
  33. ltoa( GetCurrentThreadId(), fname, 10 );
  34. FILE* fp = fopen( fname, "ab" );
  35. fputs( str, fp );
  36. fputs( "\r\n", fp );
  37. fflush( fp );
  38. fclose( fp );
  39. */
  40. }
  41. __inline void myOutputDebugStringA( void* buf, int len )
  42. {
  43. /* char fname[128];
  44. ltoa( GetCurrentThreadId(), fname, 10 );
  45. FILE* fp = fopen( fname, "ab" );
  46. fwrite( buf, len, 1, fp );
  47. fputs( "\r\n", fp );
  48. fflush( fp );
  49. fclose( fp ); */
  50. }
  51. #ifndef isnumber
  52. #define isnumber(i_n_arg) ((i_n_arg>='0')&&(i_n_arg<='9'))
  53. #endif
  54. #define sizearr(x) (sizeof(x)/sizeof(x[0]))
  55. #define xmalloc( type, len ) ((type*)malloc( (len) * sizeof(type) ) )
  56. #define xcalloc( type, len ) ((type*)calloc( (len), sizeof(type) ) )
  57. int clean_filename( char* filename )
  58. {
  59. int i;
  60. for( i = 0; filename[i]; i++ )
  61. switch( filename[i] ) {
  62. case '*':
  63. case '?':
  64. case '<':
  65. case '>':
  66. case '\\':
  67. case '/':
  68. case ':':
  69. case '"':
  70. filename[i] = '_';
  71. break;
  72. }
  73. return i;
  74. }
  75. static struct pluginlink * pl;
  76. static int lfm_loaded = 0;
  77. static char* g_folder = 0;
  78. static char* g_format = 0;
  79. static int lfm_format_filename( struct playlist_item* p_item, char* p_filename, int n_filename )
  80. {
  81. int i = 0, j;
  82. char ch;
  83. char* fmt;
  84. char *ff = NULL;
  85. if( g_folder && *g_folder ) {
  86. strncpy( p_filename, g_folder, n_filename );
  87. i = strlen( p_filename );
  88. if( i < n_filename-1 && p_filename[i-1] != '\\' )
  89. p_filename[i++] = '\\';
  90. }
  91. fmt = g_format;
  92. if( fmt == NULL ) fmt = "%a\\%t.mp3";
  93. while( ( ch = *fmt++ ) != 0 ) {
  94. if( ch == '%' ) {
  95. char* p_sz = NULL;
  96. char a_sz[32];
  97. ch = *fmt++;
  98. switch( ch ) {
  99. case 0:
  100. fmt--; break;
  101. case '%':
  102. p_sz = "%";
  103. break;
  104. case 'n': {
  105. static unsigned ndx = 0;
  106. ltoa( ndx++, a_sz, 10 );
  107. p_sz = a_sz;
  108. break;
  109. }
  110. case 'a':
  111. p_sz = p_item->artist;
  112. break;
  113. case 't':
  114. p_sz = p_item->title;
  115. break;
  116. case 'l':
  117. p_sz = p_item->album;
  118. break;
  119. }
  120. if( p_sz ) {
  121. strncpy( p_filename+i, p_sz, n_filename-i );
  122. p_filename[n_filename-1] = 0;
  123. i += clean_filename( p_filename+i );
  124. }
  125. } else
  126. if( i < n_filename ) p_filename[i++] = ch;
  127. }
  128. for( j = i-1; j >= 0; j-- )
  129. if( p_filename[j] == '\\' || p_filename[j] == '/' ) {
  130. ff = p_filename + j;
  131. break;
  132. }
  133. if( ff ) {
  134. char ch = *ff;
  135. *ff = 0;
  136. my_mkdir( p_filename );
  137. *ff = ch;
  138. }
  139. return i;
  140. }
  141. static void lfm_close_request( struct lfm_client_data* p_client )
  142. {
  143. struct lfm_filter_data* p_data;
  144. if( p_client == NULL ) return;
  145. p_data = p_client->p_data;
  146. if( p_data == NULL ) return;
  147. if( p_client->req_type == LFM_PLAYLIST ) {
  148. free( p_client->pl_xml );
  149. } else if( p_client->req_type == LFM_GET && p_client->fp ) {
  150. struct playlist_item* p_item = p_client->pl_item;
  151. if( p_item ) {
  152. char id3v1[128];
  153. memset( id3v1, 0, 128 );
  154. strcpy( id3v1, "TAG" );
  155. strncpy( id3v1+3, p_item->title, 30 );
  156. strncpy( id3v1+33, p_item->artist, 30 );
  157. strncpy( id3v1+63, p_item->album, 30 );
  158. id3v1[127] = -1;
  159. fwrite( id3v1, 128, 1, p_client->fp );
  160. }
  161. fclose( p_client->fp );
  162. /*
  163. if( p_item ) {
  164. char filename_part[512], filename[512];
  165. int i = lfm_format_filename( p_item, filename_part, sizearr(filename_part) );
  166. memcpy( filename, filename_part, sizeof(filename) );
  167. strncpy( filename_part+i, PART_SUFFIX, sizearr(filename_part)-i );
  168. rename( filename_part, filename );
  169. }
  170. */
  171. pthread_mutex_lock( &p_data->mutex );
  172. if( p_data->playlist != NULL && p_item != NULL ) {
  173. if( p_data->playlist == p_item )
  174. p_data->playlist = p_item->next;
  175. else {
  176. struct playlist_item *p_last = p_data->playlist;
  177. while( p_last->next != NULL && p_last->next != p_item )
  178. p_last = p_last->next;
  179. if( p_last->next )
  180. p_last->next = p_item->next;
  181. }
  182. if( p_item->artist ) free( p_item->artist );
  183. if( p_item->title ) free( p_item->title );
  184. if( p_item->album ) free( p_item->album );
  185. if( p_item->url ) free( p_item->url );
  186. free( p_item );
  187. }
  188. pthread_mutex_unlock( &p_data->mutex );
  189. }
  190. p_client->req_type = LFM_NONE;
  191. }
  192. static void* lfm_filter_open( void * idata, struct srvparam * param ){
  193. struct lfm_filter_data* pdata = (struct lfm_filter_data*)idata;
  194. if( pdata ){
  195. pthread_mutex_lock( &pdata->mutex );
  196. pdata->refs++;
  197. pthread_mutex_unlock( &pdata->mutex );
  198. } else {
  199. if( ( pdata = xcalloc( struct lfm_filter_data, 1 ) ) != NULL ) {
  200. pthread_mutex_init( &pdata->mutex, NULL );
  201. pdata->playlist = NULL;
  202. pdata->refs++;
  203. }
  204. }
  205. return pdata;
  206. }
  207. static FILTER_ACTION lfm_filter_client( void *fo, struct clientparam* param, void** fc )
  208. {
  209. struct lfm_filter_data* p_data;
  210. struct lfm_client_data* p_client;
  211. *fc = NULL;
  212. if( fo == NULL ) return PASS;
  213. p_data = (struct lfm_filter_data*)fo;
  214. p_client = xcalloc( struct lfm_client_data, 1 );
  215. if( p_client == NULL ) return PASS;
  216. p_client->p_data = p_data;
  217. p_client->req_type = LFM_NONE;
  218. *fc = p_client;
  219. return CONTINUE;
  220. }
  221. static void lfm_filter_clear( void *fc )
  222. {
  223. struct lfm_client_data* p_client;
  224. p_client = (struct lfm_client_data*)fc;
  225. if( p_client == NULL ) return;
  226. lfm_close_request( p_client );
  227. free( p_client );
  228. }
  229. static void lfm_filter_close(void *fo){
  230. struct lfm_filter_data* p_data;
  231. struct playlist_item* p_item;
  232. p_data = (struct lfm_filter_data*)fo;
  233. if( p_data == NULL ) return;
  234. if( --p_data->refs > 0 ) return;
  235. pthread_mutex_destroy( &p_data->mutex );
  236. p_item = p_data->playlist;
  237. while( p_item ) {
  238. struct playlist_item* p_old = p_item;
  239. p_item = p_item->next;
  240. if( p_old->artist ) free( p_old->artist );
  241. if( p_old->title ) free( p_old->title );
  242. if( p_old->album ) free( p_old->album );
  243. if( p_old->url ) free( p_old->url );
  244. free( p_old );
  245. }
  246. free( p_data );
  247. }
  248. static FILTER_ACTION lfm_filter_request( void *fc, struct clientparam *param, unsigned char** buf_p, int* bufsize_p, int offset, int* length_p )
  249. {
  250. char* p_buf = (char*)( *buf_p + offset );
  251. int n_buf = *length_p - offset;
  252. struct lfm_client_data* p_client;
  253. struct lfm_filter_data* p_data;
  254. if( p_buf == NULL || n_buf < 5 ) return CONTINUE;
  255. p_client = (struct lfm_client_data*)fc;
  256. if( p_client == NULL ) return CONTINUE;
  257. p_data = p_client->p_data;
  258. if( p_data == NULL ) return CONTINUE;
  259. pl->conf->filtermaxsize = 0;
  260. lfm_close_request( p_client );
  261. p_client->req_type = LFM_NONE;
  262. p_client->fp = NULL;
  263. if( strncasecmp( p_buf, "GET ", 4 ) != 0 ) return CONTINUE;
  264. p_buf += 4;
  265. if( strncasecmp( p_buf, "http://ws.audioscrobbler.com/radio/xspf.php?", 44 ) == 0 ) {
  266. myOutputDebugStringA1( "getting a playlist" );
  267. p_client->req_type = LFM_PLAYLIST;
  268. } else {
  269. char zzz[256];
  270. int i;
  271. struct playlist_item* p_item;
  272. for( i = 0; i < n_buf && i < 256; i++ ) {
  273. if( p_buf[i] == '\r' ) break;
  274. zzz[i] = p_buf[i];
  275. }
  276. zzz[i] = 0;
  277. myOutputDebugStringA1( zzz );
  278. pthread_mutex_lock( &p_data->mutex );
  279. p_item = p_data->playlist;
  280. while( p_item ) {
  281. if( strncasecmp( p_buf, p_item->url, p_item->url_len ) == 0 )
  282. break;
  283. p_item = p_item->next;
  284. }
  285. pthread_mutex_unlock( &p_data->mutex );
  286. if( p_item ) {
  287. myOutputDebugStringA1( "getting a known url: " );
  288. myOutputDebugStringA1( p_item->title );
  289. p_client->req_type = LFM_GET;
  290. p_client->pl_item = p_item;
  291. }
  292. }
  293. return CONTINUE;
  294. }
  295. static FILTER_ACTION lfm_filter_header_srv( void *fc, struct clientparam *param, unsigned char** buf_p, int* bufsize_p, int offset, int* length_p )
  296. {
  297. char* p_buf = (char*)( *buf_p + offset );
  298. struct lfm_client_data* p_client;
  299. int n_buf = *length_p - offset;
  300. struct lfm_filter_data* p_data;
  301. char zzz[100];
  302. int code;
  303. if( p_buf == NULL || n_buf < 9 ) return CONTINUE;
  304. p_client = (struct lfm_client_data*)fc;
  305. if( p_client == NULL ) return CONTINUE;
  306. p_data = p_client->p_data;
  307. if( p_data == NULL ) return CONTINUE;
  308. code = atoi( p_buf + 9 );
  309. sprintf( zzz, "http code: %d", code );
  310. myOutputDebugStringA1( zzz );
  311. myOutputDebugStringA( p_buf, n_buf );
  312. if( p_client->req_type == LFM_GET && ( code > 300 && code < 304 || code == 307 ) ) {
  313. p_client->req_type = LFM_REDIR;
  314. do {
  315. char* p_line = p_buf;
  316. int n_line;
  317. for(; n_buf > 0 && *p_buf != '\r'; p_buf++, n_buf-- ) {};
  318. n_line = p_buf - p_line;
  319. if( n_line <= 0 ) break;
  320. if( n_line > 10 && strncasecmp( p_line, "location: ", 10 ) == 0 ) {
  321. myOutputDebugStringA1( "redir/location: " );
  322. myOutputDebugStringA( p_line + 10, n_line - 10 );
  323. if( p_client->pl_item ) {
  324. char* p_url = p_line + 10;
  325. int n_url = n_line - 10;
  326. pthread_mutex_lock( &p_data->mutex );
  327. if( p_client->pl_item->url ) free( p_client->pl_item->url );
  328. p_client->pl_item->url = xmalloc( char, n_url + 1 + 1 );
  329. memcpy( p_client->pl_item->url, p_url, n_url );
  330. p_client->pl_item->url[n_url] = ' ';
  331. p_client->pl_item->url[n_url+1] = 0;
  332. p_client->pl_item->url_len = n_url + 1;
  333. myOutputDebugStringA1( "got a url: " );
  334. myOutputDebugStringA1( p_client->pl_item->url );
  335. pthread_mutex_unlock( &p_data->mutex );
  336. }
  337. p_client->req_type = LFM_NONE;
  338. }
  339. for(; n_buf > 0 && ( *p_buf == '\n' || *p_buf == '\r' ); p_buf++, n_buf-- ) {};
  340. } while( n_buf > 0 );
  341. }
  342. if( code == 200 && p_client->req_type == LFM_GET ) {
  343. struct playlist_item* p_item = p_client->pl_item;
  344. char filename[512];
  345. int i = lfm_format_filename( p_item, filename, sizearr(filename) );
  346. /*
  347. strncpy( filename + i, PART_SUFFIX, sizearr(filename)-i );
  348. */
  349. p_client->fp = fopen( filename, "wb" );
  350. }
  351. else if( code == 200 && p_client->req_type == LFM_PLAYLIST ) {
  352. p_client->pl_xml = xcalloc( struct xml_state, 1 );
  353. }
  354. return CONTINUE;
  355. }
  356. void playlist_analyze( struct lfm_client_data* p_client, const char* p_buf, int len );
  357. static FILTER_ACTION lfm_filter_data_srv( void *fc, struct clientparam *param, unsigned char** buf_p, int* bufsize_p, int offset, int* length_p )
  358. {
  359. char* p_buf = (char*)( *buf_p + offset );
  360. int n_buf = *length_p - offset;
  361. struct lfm_client_data* p_client;
  362. struct lfm_filter_data* p_data;
  363. myOutputDebugStringA1( "filter_data_srv" );
  364. myOutputDebugStringA( p_buf, n_buf );
  365. if( p_buf == NULL || n_buf < 1 ) return CONTINUE;
  366. p_client = (struct lfm_client_data*)fc;
  367. if( p_client == NULL ) return CONTINUE;
  368. p_data = p_client->p_data;
  369. if( p_data == NULL ) return CONTINUE;
  370. if( p_client->req_type == LFM_PLAYLIST )
  371. myOutputDebugStringA1( "filter_data_srv: playlist" );
  372. else if( p_client->req_type == LFM_GET ) {
  373. myOutputDebugStringA1( "filter_data_srv: retrieving" );
  374. if( p_client->fp == NULL )
  375. myOutputDebugStringA1( "but no file allocated" );
  376. }
  377. if( p_client->req_type == LFM_PLAYLIST )
  378. playlist_analyze( p_client, p_buf, n_buf );
  379. if( p_client->fp )
  380. fwrite( p_buf, n_buf, 1, p_client->fp );
  381. return CONTINUE;
  382. }
  383. enum XmlState
  384. {
  385. XML_TEXT,
  386. XML_TAGNAME,
  387. XML_ATTRNAME_W,
  388. XML_ATTRNAME,
  389. XML_ATTRVALUE_W,
  390. XML_ATTRVALUE,
  391. XML_TAGCLOSE,
  392. XML_TAGOPENCLOSE,
  393. };
  394. int xml_get_token_id( char** table, char* token )
  395. {
  396. int i;
  397. char* psz;
  398. for( psz = token; *psz; psz++ )
  399. *psz = tolower( *psz );
  400. for( i = 0; *table; i++, table++ )
  401. if( strcmp( *table, token ) == 0 ) return i;
  402. return -1;
  403. }
  404. enum {
  405. XT_PLAYLIST,
  406. XT_TRACK_LIST,
  407. XT_TRACK,
  408. XT_LOCATION,
  409. XT_CREATOR,
  410. XT_ALBUM,
  411. XT_TITLE
  412. };
  413. char* xt_list[] = {
  414. "playlist",
  415. "tracklist",
  416. "track",
  417. "location",
  418. "creator",
  419. "album",
  420. "title",
  421. NULL
  422. };
  423. void playlist_analyze( struct lfm_client_data* p_client, const char* p_buf, int len )
  424. {
  425. struct xml_state* xs = p_client->pl_xml;
  426. int n_buf = len;
  427. if( xs == NULL ) return;
  428. while( n_buf > 0 ) {
  429. enum {
  430. XMS_NONE,
  431. XMS_TAG,
  432. XMS_ATTR_NAME,
  433. XMS_ATTR_VALUE,
  434. XMS_TEXT
  435. } gotta = XMS_NONE;
  436. char ch = *p_buf++;
  437. n_buf--;
  438. lbl_retry:
  439. switch( xs->state ) {
  440. case XML_TAGNAME:
  441. if( ch == '>' ) {
  442. xs->state = XML_TEXT;
  443. gotta = XMS_TAG;
  444. break;
  445. } else if( ch == '/' ) {
  446. if( xs->n_token == 0 )
  447. xs->closing = 1;
  448. else {
  449. xs->state = XML_ATTRNAME_W;
  450. gotta = XMS_TAG;
  451. xs->n_token--;
  452. }
  453. break;
  454. } else if( isspace( ch ) ) {
  455. xs->state = XML_ATTRNAME_W;
  456. gotta = XMS_TAG;
  457. break;
  458. }
  459. if( xs->n_token < TOKEN_MAXLEN ) xs->p_token[xs->n_token++] = ch;
  460. break;
  461. case XML_ATTRNAME_W:
  462. if( ch == '>' ) {
  463. xs->state = XML_TEXT;
  464. break;
  465. } else if( ch == '/' ) {
  466. xs->closing = 1;
  467. xs->state = XML_TEXT;
  468. gotta = XMS_TAG;
  469. break;
  470. } else if( !isspace( ch ) ) {
  471. xs->state = XML_ATTRNAME;
  472. goto lbl_retry;
  473. }
  474. break;
  475. case XML_ATTRNAME:
  476. if( ch == '=' ) {
  477. xs->state = XML_ATTRVALUE_W;
  478. gotta = XMS_ATTR_NAME;
  479. break;
  480. }
  481. if( xs->n_token < TOKEN_MAXLEN ) xs->p_token[xs->n_token++] = ch;
  482. break;
  483. case XML_ATTRVALUE_W:
  484. if( ch == '"' || ch == '\'' ) {
  485. xs->sep = ch;
  486. xs->state = XML_ATTRVALUE;
  487. break;
  488. }
  489. break;
  490. case XML_ATTRVALUE:
  491. if( ch == xs->sep ) {
  492. xs->state = XML_ATTRNAME_W;
  493. gotta = XMS_ATTR_VALUE;
  494. break;
  495. }
  496. if( xs->n_token < TOKEN_MAXLEN ) xs->p_token[xs->n_token++] = ch;
  497. break;
  498. case XML_TEXT:
  499. if( ch == '<' ) {
  500. xs->state = XML_TAGNAME;
  501. gotta = XMS_TEXT;
  502. break;
  503. }
  504. if( xs->n_token < TOKEN_MAXLEN ) xs->p_token[xs->n_token++] = ch;
  505. break;
  506. }
  507. if( gotta != XMS_NONE ) {
  508. xs->p_token[xs->n_token] = 0;
  509. switch( gotta ) {
  510. case XMS_TAG: {
  511. int id = xml_get_token_id( xt_list, xs->p_token );
  512. if( !xs->closing ) {
  513. if( xs->level < sizeof(xs->tree) ) xs->tree[xs->level] = id;
  514. xs->level++;
  515. if( id == XT_TRACK && xs->level == 3 && xs->tree[1] == XT_TRACK_LIST )
  516. xs->p_item = xcalloc( struct playlist_item, 1 );
  517. } else {
  518. if( xs->level > 0 ) xs->level--;
  519. if( id == XT_TRACK && xs->level == 2 && xs->tree[1] == XT_TRACK_LIST ) {
  520. char zzz[1024];
  521. _snprintf( zzz, 1024, "artist: <%s>, title: <%s>, url: <%s>",
  522. xs->p_item->artist, xs->p_item->title, xs->p_item->url );
  523. myOutputDebugStringA1( zzz );
  524. pthread_mutex_lock( &p_client->p_data->mutex );
  525. xs->p_item->next = p_client->p_data->playlist;
  526. p_client->p_data->playlist = xs->p_item;
  527. pthread_mutex_unlock( &p_client->p_data->mutex );
  528. }
  529. xs->closing = 0;
  530. }
  531. break;
  532. }
  533. case XMS_ATTR_NAME:
  534. break;
  535. case XMS_ATTR_VALUE:
  536. break;
  537. case XMS_TEXT:
  538. if( xs->level == 4 && xs->tree[1] == XT_TRACK_LIST && xs->tree[2] == XT_TRACK ) {
  539. struct playlist_item* item = xs->p_item;
  540. if( item )
  541. switch( xs->tree[3] ) {
  542. case XT_LOCATION:
  543. item->url_len = xs->n_token;
  544. item->url = xmalloc( char, item->url_len + 1 + 1);
  545. memcpy( item->url, xs->p_token, item->url_len );
  546. item->url[item->url_len++] = ' ';
  547. item->url[item->url_len] = 0;
  548. break;
  549. case XT_CREATOR:
  550. item->artist = strdup( xs->p_token );
  551. break;
  552. case XT_ALBUM:
  553. item->album = strdup( xs->p_token );
  554. break;
  555. case XT_TITLE:
  556. item->title = strdup( xs->p_token );
  557. break;
  558. }
  559. }
  560. break;
  561. }
  562. xs->n_token = 0;
  563. }
  564. }
  565. }
  566. static struct filter lfm_filter = {
  567. NULL,
  568. "last.fm spy",
  569. NULL,
  570. lfm_filter_open,
  571. lfm_filter_client,
  572. lfm_filter_request,
  573. NULL,
  574. lfm_filter_header_srv,
  575. NULL,
  576. NULL,
  577. lfm_filter_data_srv,
  578. lfm_filter_clear,
  579. lfm_filter_close
  580. };
  581. static int h_lfm_folder( int argc, unsigned char **argv )
  582. {
  583. if( g_folder ) free( g_folder );
  584. g_folder = strdup( (char*)argv[1] );
  585. return 0;
  586. }
  587. static int h_lfm_format( int argc, unsigned char **argv )
  588. {
  589. if( g_format ) free( g_format );
  590. g_format = strdup( (char*)argv[1] );
  591. return 0;
  592. }
  593. static struct commands lfm_commandhandlers[] = {
  594. {lfm_commandhandlers+1, "lfm_folder", h_lfm_folder, 2, 2},
  595. {NULL, "lfm_format", h_lfm_format, 2, 2}
  596. };
  597. #ifdef __cplusplus
  598. extern "C" {
  599. #endif
  600. #ifdef WATCOM
  601. #pragma aux lfm_plugin "*" parm caller [ ] value struct float struct routine [eax] modify [eax ecx edx]
  602. #undef PLUGINCALL
  603. #define PLUGINCALL
  604. #endif
  605. PLUGINAPI int PLUGINCALL lfm_plugin( struct pluginlink * pluginlink, int argc, char** argv )
  606. {
  607. pl = pluginlink;
  608. myOutputDebugStringA1( "lfm_plugin" );
  609. if( !lfm_loaded ) {
  610. lfm_loaded = 1;
  611. lfm_filter.next = pl->conf->filters;
  612. pl->conf->filters = &lfm_filter;
  613. lfm_commandhandlers[1].next = pl->commandhandlers->next;
  614. pl->commandhandlers->next = lfm_commandhandlers;
  615. }
  616. return 0;
  617. }
  618. #ifdef __cplusplus
  619. }
  620. #endif