2.5: warning fixes
[blender-staging.git] / source / blender / python / BPY_menus.c
1 /* 
2  * $Id: BPY_menus.c 12932 2007-12-17 20:21:06Z theeth $
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * This is a new part of Blender.
27  *
28  * Contributor(s): Willian P. Germano, Michael Reimpell
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31 */
32
33 /* 
34  *This is the main file responsible for having bpython scripts accessible
35  * from Blender menus.  To know more, please start with its header file.
36  */
37
38 #include "BPY_menus.h"
39
40 #include <Python.h>
41 #ifndef WIN32
42   #include <dirent.h>
43 #else
44   #include "BLI_winstuff.h"
45 #endif
46 #include "BKE_global.h"
47 #include "BKE_utildefines.h"
48 #include "BLI_blenlib.h"
49 #include "MEM_guardedalloc.h"
50 #include "DNA_userdef_types.h"  /* for U.pythondir */
51 #include "api2_2x/EXPP_interface.h" /* for bpy_gethome() */
52
53 #define BPYMENU_DATAFILE "Bpymenus"
54 #define MAX_DIR_DEPTH 4 /* max depth for traversing scripts dirs */
55 #define MAX_DIR_NUMBER 30 /* max number of dirs in scripts dirs trees */
56
57 static int DEBUG;
58 static int Dir_Depth;
59 static int Dirs_Number;
60
61 /* BPyMenuTable holds all registered pymenus, as linked lists for each menu
62  * where they can appear (see PYMENUHOOKS enum in BPY_menus.h).
63 */
64 BPyMenu *BPyMenuTable[PYMENU_TOTAL];
65
66 static int bpymenu_group_atoi( char *str )
67 {
68         if( !strcmp( str, "Export" ) )
69                 return PYMENU_EXPORT;
70         else if( !strcmp( str, "Import" ) )
71                 return PYMENU_IMPORT;
72         else if( !strcmp( str, "Help" ) )
73                 return PYMENU_HELP;
74         else if( !strcmp( str, "HelpWebsites" ) )
75                 return PYMENU_HELPWEBSITES;
76         else if( !strcmp( str, "HelpSystem" ) )
77                 return PYMENU_HELPSYSTEM;
78         else if( !strcmp( str, "Render" ) )
79                 return PYMENU_RENDER;
80         else if( !strcmp( str, "System" ) )
81                 return PYMENU_SYSTEM;
82         else if( !strcmp( str, "Object" ) )
83                 return PYMENU_OBJECT;
84         else if( !strcmp( str, "Mesh" ) )
85                 return PYMENU_MESH;
86         else if( !strncmp( str, "Theme", 5 ) )
87                 return PYMENU_THEMES;
88         else if( !strcmp( str, "Add" ) )
89                 return PYMENU_ADD;
90         else if( !strcmp( str, "Wizards" ) )
91                 return PYMENU_WIZARDS;
92         else if( !strcmp( str, "Animation" ) )
93                 return PYMENU_ANIMATION;
94         else if( !strcmp( str, "Materials" ) )
95                 return PYMENU_MATERIALS;
96         else if( !strcmp( str, "UV" ) )
97                 return PYMENU_UV;
98         else if( !strcmp( str, "Image" ) )
99                 return PYMENU_IMAGE;
100         else if( !strcmp( str, "FaceSelect" ) )
101                 return PYMENU_FACESELECT;
102         else if( !strcmp( str, "WeightPaint" ) )
103                 return PYMENU_WEIGHTPAINT;
104         else if( !strcmp( str, "VertexPaint" ) )
105                 return PYMENU_VERTEXPAINT;
106         else if( !strcmp( str, "UVCalculation" ) )
107                 return PYMENU_UVCALCULATION;
108         else if( !strcmp( str, "Armature" ) )
109                 return PYMENU_ARMATURE;
110         else if( !strcmp( str, "ScriptTemplate" ) )
111                 return PYMENU_SCRIPTTEMPLATE;
112         else if( !strcmp( str, "MeshFaceKey" ) )
113                 return PYMENU_MESHFACEKEY;
114         else if( !strcmp( str, "AddMesh" ) )
115                 return PYMENU_ADDMESH;
116         /* "Misc" or an inexistent group name: use misc */
117         else
118                 return PYMENU_MISC;
119 }
120
121 char *BPyMenu_group_itoa( short menugroup )
122 {
123         switch ( menugroup ) {
124         case PYMENU_EXPORT:
125                 return "Export";
126                 break;
127         case PYMENU_IMPORT:
128                 return "Import";
129                 break;
130         case PYMENU_ADD:
131                 return "Add";
132                 break;
133         case PYMENU_HELP:
134                 return "Help";
135                 break;
136         case PYMENU_HELPWEBSITES:
137                 return "HelpWebsites";
138                 break;
139         case PYMENU_HELPSYSTEM:
140                 return "HelpSystem";
141                 break;
142         case PYMENU_RENDER:
143                 return "Render";
144                 break;
145         case PYMENU_SYSTEM:
146                 return "System";
147                 break;
148         case PYMENU_OBJECT:
149                 return "Object";
150                 break;
151         case PYMENU_MESH:
152                 return "Mesh";
153                 break;
154         case PYMENU_THEMES:
155                 return "Themes";
156                 break;
157         case PYMENU_WIZARDS:
158                 return "Wizards";
159                 break;
160         case PYMENU_ANIMATION:
161                 return "Animation";
162                 break;
163         case PYMENU_MATERIALS:
164                 return "Materials";
165                 break;
166         case PYMENU_UV:
167                 return "UV";
168                 break;
169         case PYMENU_IMAGE:
170                 return "Image";
171                 break;
172         case PYMENU_FACESELECT:
173                 return "FaceSelect";
174                 break;
175         case PYMENU_WEIGHTPAINT:
176                 return "WeightPaint";
177                 break;
178         case PYMENU_VERTEXPAINT:
179                 return "VertexPaint";
180                 break;
181         case PYMENU_UVCALCULATION:
182                 return "UVCalculation";
183                 break;
184         case PYMENU_ARMATURE:
185                 return "Armature";
186                 break;
187         case PYMENU_SCRIPTTEMPLATE:
188                 return "ScriptTemplate";
189                 break;
190         case PYMENU_MESHFACEKEY:
191                 return "MeshFaceKey";
192                 break;
193         case PYMENU_ADDMESH:
194                 return "AddMesh";
195                 break;
196         case PYMENU_MISC:
197                 return "Misc";
198                 break;
199         }
200         return NULL;
201 }
202
203 /* BPyMenu_CreatePupmenuStr:
204  * build and return a meaninful string to be used by pupmenu().  The
205  * string is made of a bpymenu name as title and its submenus as possible
206  * choices for the user.
207 */
208 char *BPyMenu_CreatePupmenuStr( BPyMenu * pym, short menugroup )
209 {
210         BPySubMenu *pysm = pym->submenus;
211         char str[1024], str2[100];
212         int i = 0, rlen;
213
214         if( !pym || !pysm )
215                 return NULL;
216
217         str[0] = '\0';
218
219         PyOS_snprintf( str2, sizeof( str2 ), "%s: %s%%t",
220                        BPyMenu_group_itoa( menugroup ), pym->name );
221         strcat( str, str2 );
222
223         while( pysm ) {
224                 PyOS_snprintf( str2, sizeof( str2 ), "|%s%%x%d", pysm->name,
225                                i );
226                 rlen = sizeof( str ) - strlen( str );
227                 strncat( str, str2, rlen );
228                 i++;
229                 pysm = pysm->next;
230         }
231
232         return BLI_strdup( str );
233 }
234
235 static void bpymenu_RemoveAllSubEntries( BPySubMenu * smenu )
236 {
237         BPySubMenu *tmp;
238
239         while( smenu ) {
240                 tmp = smenu->next;
241                 if( smenu->name )
242                         MEM_freeN( smenu->name );
243                 if( smenu->arg )
244                         MEM_freeN( smenu->arg );
245                 MEM_freeN( smenu );
246                 smenu = tmp;
247         }
248         return;
249 }
250
251 void BPyMenu_RemoveAllEntries( void )
252 {
253         BPyMenu *tmp, *pymenu;
254         int i;
255
256         for( i = 0; i < PYMENU_TOTAL; i++ ) {
257                 pymenu = BPyMenuTable[i];
258                 while( pymenu ) {
259                         tmp = pymenu->next;
260                         if( pymenu->name )
261                                 MEM_freeN( pymenu->name );
262                         if( pymenu->filename )
263                                 MEM_freeN( pymenu->filename );
264                         if( pymenu->tooltip )
265                                 MEM_freeN( pymenu->tooltip );
266                         if( pymenu->submenus )
267                                 bpymenu_RemoveAllSubEntries( pymenu->
268                                                              submenus );
269                         MEM_freeN( pymenu );
270                         pymenu = tmp;
271                 }
272                 BPyMenuTable[i] = NULL;
273         }
274
275         Dirs_Number = 0;
276         Dir_Depth = 0;
277
278         return;
279 }
280
281 static BPyMenu *bpymenu_FindEntry( short group, char *name )
282 {
283         BPyMenu *pymenu;
284
285         if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) )
286                 return NULL;
287
288         pymenu = BPyMenuTable[group];
289
290         while( pymenu ) {
291                 if( !strcmp( pymenu->name, name ) )
292                         return pymenu;
293                 pymenu = pymenu->next;
294         }
295
296         return NULL;
297 }
298
299 /* BPyMenu_GetEntry:
300  * given a group and a position, return the entry in that position from
301  * that group.
302 */
303 BPyMenu *BPyMenu_GetEntry( short group, short pos )
304 {
305         BPyMenu *pym = NULL;
306
307         if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) )
308                 return NULL;
309
310         pym = BPyMenuTable[group];
311
312         while( pos-- ) {
313                 if( pym )
314                         pym = pym->next;
315                 else
316                         break;
317         }
318
319         return pym;             /* found entry or NULL */
320 }
321
322 static void bpymenu_set_tooltip( BPyMenu * pymenu, char *tip )
323 {
324         if( !pymenu )
325                 return;
326
327         if( pymenu->tooltip )
328                 MEM_freeN( pymenu->tooltip );
329         pymenu->tooltip = BLI_strdup( tip );
330
331         return;
332 }
333
334 /* bpymenu_AddEntry:
335  * try to find an existing pymenu entry with the given type and name;
336  * if found, update it with new info, otherwise create a new one and fill it.
337  */
338 static BPyMenu *bpymenu_AddEntry( short group, short version, char *name,
339         char *fname, int is_userdir, char *tooltip )
340 {
341         BPyMenu *menu, *next = NULL, **iter;
342         int nameclash = 0;
343
344         if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) )
345                 return NULL;
346         if( !name || !fname )
347                 return NULL;
348
349         menu = bpymenu_FindEntry( group, name );        /* already exists? */
350
351         /* if a menu with this name already exists in the same group:
352          * - if one script is in the default dir and the other in U.pythondir,
353          *   accept and let the new one override the other.
354          * - otherwise, report the error and return NULL. */
355         if( menu ) {
356                 if( menu->dir < is_userdir ) {  /* new one is in U.pythondir */
357                         nameclash = 1;
358                         if( menu->name )
359                                 MEM_freeN( menu->name );
360                         if( menu->filename )
361                                 MEM_freeN( menu->filename );
362                         if( menu->tooltip )
363                                 MEM_freeN( menu->tooltip );
364                         if( menu->submenus )
365                                 bpymenu_RemoveAllSubEntries( menu->submenus );
366                         next = menu->next;
367                 } else {        /* they are in the same dir */
368                         if (DEBUG) {
369                                 fprintf(stderr, "\n\
370 Warning: script %s's menu name is already in use.\n\
371 Edit the script and change its \n\
372 Name: '%s'\n\
373 field, please.\n\
374 Note: if you really want to have two scripts for the same menu with\n\
375 the same name, keep one in the default dir and the other in\n\
376 the user defined dir (only the later will be registered).\n", fname, name);
377                         }
378                         return NULL;
379                 }
380         } else
381                 menu = MEM_mallocN( sizeof( BPyMenu ), "pymenu" );
382
383         if( !menu )
384                 return NULL;
385
386         menu->name = BLI_strdup( name );
387         menu->version = version;
388         menu->filename = BLI_strdup( fname );
389         menu->tooltip = NULL;
390         if( tooltip )
391                 menu->tooltip = BLI_strdup( tooltip );
392         menu->dir = is_userdir;
393         menu->submenus = NULL;
394         menu->next = next;      /* non-NULL if menu already existed */
395
396         if( nameclash )
397                 return menu;    /* no need to place it, it's already at the list */
398         else {  /* insert the new entry in its correct position at the table */
399                 BPyMenu *prev = NULL;
400                 char *s = NULL;
401
402                 iter = &BPyMenuTable[group];
403                 while( *iter ) {
404                         s = ( *iter )->name;
405                         if( s )
406                                 if( strcmp( menu->name, s ) < 0 )
407                                         break;  /* sort by names */
408                         prev = *iter;
409                         iter = &( ( *iter )->next );
410                 }
411
412                 if( *iter ) {   /* prepend */
413                         menu->next = *iter;
414                         if( prev )
415                                 prev->next = menu;
416                         else
417                                 BPyMenuTable[group] = menu;     /* is first entry */
418                 } else
419                         *iter = menu;   /* append */
420         }
421
422         return menu;
423 }
424
425 /* bpymenu_AddSubEntry:
426  * add a submenu to an existing python menu.
427  */
428 static int bpymenu_AddSubEntry( BPyMenu * mentry, char *name, char *arg )
429 {
430         BPySubMenu *smenu, **iter;
431
432         smenu = MEM_mallocN( sizeof( BPySubMenu ), "pysubmenu" );
433         if( !smenu )
434                 return -1;
435
436         smenu->name = BLI_strdup( name );
437         smenu->arg = BLI_strdup( arg );
438         smenu->next = NULL;
439
440         if( !smenu->name || !smenu->arg )
441                 return -1;
442
443         iter = &( mentry->submenus );
444         while( *iter )
445                 iter = &( ( *iter )->next );
446
447         *iter = smenu;
448
449         return 0;
450 }
451
452 /* bpymenu_CreateFromFile:
453  * parse the bpymenus data file where Python menu data is stored;
454  * based on this data, create and fill the pymenu structs.
455  */
456 static int bpymenu_CreateFromFile( void )
457 {
458         FILE *fp;
459         char line[255], w1[255], w2[255], tooltip[255], *tip;
460         char *homedir = NULL;
461         int parsing, version, is_userdir;
462         short group;
463         BPyMenu *pymenu = NULL;
464
465         /* init global bpymenu table (it is a list of pointers to struct BPyMenus
466          * for each available cathegory: import, export, etc.) */
467         for( group = 0; group < PYMENU_TOTAL; group++ )
468                 BPyMenuTable[group] = NULL;
469
470         /* let's try to open the file with bpymenu data */
471         homedir = bpy_gethome(0);
472         if (!homedir) {
473                 if( DEBUG )
474                         fprintf(stderr,
475                                 "BPyMenus error: couldn't open config file Bpymenus: no home dir.\n");
476                 return -1;
477         }
478
479         BLI_make_file_string( "/", line, homedir, BPYMENU_DATAFILE );
480
481         fp = fopen( line, "rb" );
482
483         if( !fp ) {
484                 if( DEBUG )
485                         fprintf(stderr, "BPyMenus error: couldn't open config file %s.\n", line );
486                 return -1;
487         }
488
489         fgets( line, 255, fp ); /* header */
490
491         /* check if the U.pythondir we saved at the file is different from the
492          * current one.  If so, return to force updating from dirs */
493         w1[0] = '\0';
494         fscanf( fp, "# User defined scripts dir: %[^\n]\n", w1 );
495         if( w1 ) {
496                 char upythondir[FILE_MAXDIR];
497
498                 BLI_strncpy(upythondir, U.pythondir, FILE_MAXDIR);
499                 BLI_convertstringcode(upythondir, G.sce, 0);
500                 if( strcmp( w1, upythondir ) != 0 )
501                         return -1;
502                 w1[0] = '\0';
503         }
504
505         while( fgets( line, 255, fp ) ) {       /* parsing file lines */
506
507                 switch ( line[0] ) {    /* check first char */
508                 case '#':       /* comment */
509                         continue;
510                         break;
511                 case '\n':
512                         continue;
513                         break;
514                 default:
515                         parsing = sscanf( line, "%s {\n", w1 ); /* menu group */
516                         break;
517                 }
518
519                 if( parsing == 1 ) {    /* got menu group string */
520                         group = (short)bpymenu_group_atoi( w1 );
521                         if( group < 0 && DEBUG ) {      /* invalid type */
522                                 fprintf(stderr,
523                                         "BPyMenus error parsing config file: wrong group: %s,\n\
524 will use 'Misc'.\n", w1 );
525                         }
526                 } else
527                         continue;
528
529                 for(;;) {
530                         tip = NULL;     /* optional tooltip */
531                         fgets( line, 255, fp );
532                         if( line[0] == '}' )
533                                 break;
534                         else if( line[0] == '\n' )
535                                 continue;
536                         else if( line[0] == '\'' ) {    /* menu entry */
537                                 parsing =
538                                         sscanf( line,
539                                                 "'%[^']' %d %s %d '%[^']'\n",
540                                                 w1, &version, w2, &is_userdir,
541                                                 tooltip );
542
543                                 if( parsing <= 0 ) {    /* invalid line, get rid of it */
544                                         fgets( line, 255, fp );
545                                 } else if( parsing == 5 )
546                                         tip = tooltip;  /* has tooltip */
547
548                                 pymenu = bpymenu_AddEntry( group,
549                                                            ( short ) version,
550                                                            w1, w2, is_userdir,
551                                                            tip );
552                                 if( !pymenu ) {
553                                         puts( "BPyMenus error: couldn't create bpymenu entry.\n" );
554                                         fclose( fp );
555                                         return -1;
556                                 }
557                         } else if( line[0] == '|' && line[1] == '_' ) { /* menu sub-entry */
558                                 if( !pymenu )
559                                         continue;       /* no menu yet, skip this line */
560                                 sscanf( line, "|_%[^:]: %s\n", w1, w2 );
561                                 bpymenu_AddSubEntry( pymenu, w1, w2 );
562                         }
563                 }
564         }
565
566         fclose( fp );
567         return 0;
568 }
569
570 /* bpymenu_WriteDataFile:
571  * writes the registered scripts info to the user's home dir, for faster
572  * access when the scripts dir hasn't changed.
573 */
574 static void bpymenu_WriteDataFile( void )
575 {
576         BPyMenu *pymenu;
577         BPySubMenu *smenu;
578         FILE *fp;
579         char fname[FILE_MAXDIR], *homedir;
580         int i;
581
582         homedir = bpy_gethome(0);
583
584         if (!homedir) {
585                 if( DEBUG )
586                         fprintf(stderr,
587                                 "BPyMenus error: couldn't write Bpymenus file: no home dir.\n\n");
588                 return;
589         }
590
591         BLI_make_file_string( "/", fname, homedir, BPYMENU_DATAFILE );
592
593         fp = fopen( fname, "w" );
594         if( !fp ) {
595                 if( DEBUG )
596                         fprintf(stderr, "BPyMenus error: couldn't write %s file.\n\n",
597                                 fname );
598                 return;
599         }
600
601         fprintf( fp,
602                  "# Blender: registered menu entries for bpython scripts\n" );
603
604         if (U.pythondir[0] != '\0' &&
605                         strcmp(U.pythondir, "/") != 0 && strcmp(U.pythondir, "//") != 0)
606         {
607                 char upythondir[FILE_MAXDIR];
608
609                 BLI_strncpy(upythondir, U.pythondir, FILE_MAXDIR);
610                 BLI_convertstringcode(upythondir, G.sce, 0);
611                 fprintf( fp, "# User defined scripts dir: %s\n", upythondir );
612         }
613
614         for( i = 0; i < PYMENU_TOTAL; i++ ) {
615                 pymenu = BPyMenuTable[i];
616                 if( !pymenu )
617                         continue;
618                 fprintf( fp, "\n%s {\n", BPyMenu_group_itoa( (short)i ) );
619                 while( pymenu ) {
620                         fprintf( fp, "'%s' %d %s %d", pymenu->name,
621                                  pymenu->version, pymenu->filename,
622                                  pymenu->dir );
623                         if( pymenu->tooltip )
624                                 fprintf( fp, " '%s'\n", pymenu->tooltip );
625                         else
626                                 fprintf( fp, "\n" );
627                         smenu = pymenu->submenus;
628                         while( smenu ) {
629                                 fprintf( fp, "|_%s: %s\n", smenu->name,
630                                          smenu->arg );
631                                 smenu = smenu->next;
632                         }
633                         pymenu = pymenu->next;
634                 }
635                 fprintf( fp, "}\n" );
636         }
637
638         fclose( fp );
639         return;
640 }
641
642 /* BPyMenu_PrintAllEntries:
643  * useful for debugging.
644  */
645 void BPyMenu_PrintAllEntries( void )
646 {
647         BPyMenu *pymenu;
648         BPySubMenu *smenu;
649         int i;
650
651         printf( "# Blender: registered menu entries for bpython scripts\n" );
652
653         for( i = 0; i < PYMENU_TOTAL; i++ ) {
654                 pymenu = BPyMenuTable[i];
655                 printf( "\n%s {\n", BPyMenu_group_itoa( (short)i ) );
656                 while( pymenu ) {
657                         printf( "'%s' %d %s %d", pymenu->name, pymenu->version,
658                                 pymenu->filename, pymenu->dir );
659                         if( pymenu->tooltip )
660                                 printf( " '%s'\n", pymenu->tooltip );
661                         else
662                                 printf( "\n" );
663                         smenu = pymenu->submenus;
664                         while( smenu ) {
665                                 printf( "|_%s: %s\n", smenu->name,
666                                         smenu->arg );
667                                 smenu = smenu->next;
668                         }
669                         pymenu = pymenu->next;
670                 }
671                 printf( "}\n" );
672         }
673 }
674
675 /* bpymenu_ParseFile:
676  * recursively scans folders looking for scripts to register.
677  *
678  * This function scans the scripts directory looking for .py files with the
679  * right header and menu info, using that to fill the bpymenu structs.
680  * is_userdir defines if the script is in the default scripts dir or the
681  * user defined one (U.pythondir: is_userdir == 1).
682  * Speed is important.
683  *
684  * The first line of the script must be '#!BPY'.
685  * The header registration lines must appear between the first pair of
686  * '\"\"\"' and follow this order (the single-quotes are part of
687  * the format):
688  *
689  * # \"\"\"<br>
690  * # Name: 'script name for the menu'
691  * # Blender: <code>short int</code> (minimal Blender version)
692  * # Group: 'group name' (defines menu)
693  * # Submenu: 'submenu name' related_1word_arg
694  * # Tooltip: 'tooltip for the menu'
695  * # \"\"\"
696  *
697  * Notes:
698  *
699  * - Commenting out header lines with "#" is optional, but recommended.
700  * - There may be more than one submenu line, or none:
701  *   submenus and the tooltip are optional;
702  * - The Blender version is the same number reported by 
703  *   Blender.Get('version') in BPython or G.version in C;
704  * - Line length must be less than 99.
705  */
706 static int bpymenu_ParseFile(FILE *file, char *fname, int is_userdir)
707 {
708         char line[100];
709         char head[100];
710         char middle[100];
711         char tail[100];
712         int matches;
713         int parser_state;
714
715         char script_name[100];
716         int script_version = 1;
717         int script_group;
718
719         BPyMenu *scriptMenu = NULL;
720
721         if (file != NULL) {
722                 parser_state = 1; /* state of parser, 0 to terminate */
723
724                 while ((parser_state != 0) && (fgets(line, 100, file) != NULL)) {
725
726                         switch (parser_state) {
727
728                                 case 1: /* !BPY */
729                                         if (strncmp(line, "#!BPY", 5) == 0) {
730                                                 parser_state++;
731                                         } else {
732                                                 parser_state = 0;
733                                         }
734                                         break;
735
736                                 case 2: /* \"\"\" */
737                                         if ((strstr(line, "\"\"\""))) {
738                                                 parser_state++;
739                                         }
740                                         break;
741
742                                 case 3: /* Name: 'script name for the menu' */
743                                         matches = sscanf(line, "%[^']'%[^']'%c", head, script_name, tail);
744                                         if ((matches == 3) && (strstr(head, "Name:") != NULL)) {
745                                                 parser_state++;
746                                         } else {
747                                                 if (DEBUG)
748                                                         fprintf(stderr, "BPyMenus error: Wrong 'Name' line: %s\n", fname);
749                                                 parser_state = 0;
750                                         }
751                                         break;
752
753                                 case 4: /* Blender: <short int> */
754                                         matches = sscanf(line, "%[^1234567890]%i%c", head, &script_version,
755                                                 tail);
756                                         if (matches == 3) {
757                                                 parser_state++;
758                                         } else {
759                                                 if (DEBUG)
760                                                         fprintf(stderr,"BPyMenus error: Wrong 'Blender' line: %s\n",fname);
761                                                 parser_state = 0;
762                                         }
763                                         break;
764
765                                 case 5: /* Group: 'group name' */
766                                         matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail);
767                                         if ((matches == 3) && (strstr(head, "Group:") != NULL)) {
768                                                 script_group = bpymenu_group_atoi(middle);
769                                                 if (script_group < 0) {
770                                                         if (DEBUG)
771                                                                 fprintf(stderr, "BPyMenus error: Unknown group \"%s\": %s\n",
772                                                                         middle, fname);
773                                                         parser_state = 0;
774                                                 }
775                                                 
776                                                 else { /* register script */
777                                                         scriptMenu = bpymenu_AddEntry((short)script_group,
778                                                                 (short int)script_version, script_name, fname, is_userdir,NULL);
779                                                         if (scriptMenu == NULL) {
780                                                                 if (DEBUG)
781                                                                         fprintf(stderr,
782                                                                                 "BPyMenus error: Couldn't create entry for: %s\n", fname);
783                                                                 parser_state = 0;
784                                                         } else {
785                                                                 parser_state++;
786                                                         }
787                                                 }
788
789                                         } else {
790                                                 if (DEBUG)
791                                                         fprintf(stderr, "BPyMenus error: Wrong 'Group' line: %s\n",fname);
792                                                 parser_state = 0;
793                                         }
794                                         break;
795
796                                 case 6: /* optional elements */
797                                         /* Submenu: 'submenu name' related_1word_arg */
798                                         matches = sscanf(line, "%[^']'%[^']'%s\n", head, middle, tail);
799                                         if ((matches == 3) && (strstr(head, "Submenu:") != NULL)) {
800                                                 bpymenu_AddSubEntry(scriptMenu, middle, tail);
801                                         } else {
802                                                 /* Tooltip: 'tooltip for the menu */
803                                                 matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail);
804                                                 if ((matches == 3) && ((strstr(head, "Tooltip:") != NULL) ||
805                                                         (strstr(head, "Tip:") != NULL))) {
806                                                         bpymenu_set_tooltip(scriptMenu, middle);
807                                                 }
808                                                 parser_state = 0;
809                                         }
810                                         break;
811
812                                 default:
813                                         parser_state = 0;
814                                         break;
815                         }
816                 }
817         }
818
819         else { /* shouldn't happen, it's checked in bpymenus_ParseDir */
820                 if (DEBUG)
821                         fprintf(stderr, "BPyMenus error: Couldn't open %s.\n", fname);
822                 return -1;
823         }
824
825         return 0;
826 }
827
828 /* bpymenu_ParseDir:
829  * recursively scans folders looking for scripts to register.
830  *
831  * This function scans the scripts directory looking for .py files with the
832  * right header and menu info.
833  * - is_userdir defines if the script is in the default scripts dir or the
834  * user defined one (U.pythondir: is_userdir == 1);
835  * - parentdir is the parent dir name to store as part of the script filename,
836  * if we're down a subdir.
837  * Speed is important.
838  */
839 static int bpymenu_ParseDir(char *dirname, char *parentdir, int is_userdir )
840 {
841         DIR *dir; 
842         FILE *file = NULL;
843         struct dirent *de;
844         struct stat status;
845         char *file_extension;
846         char path[FILE_MAX];
847         char subdir[FILE_MAX];
848         char *s = NULL;
849         
850         dir = opendir(dirname);
851
852         if (dir != NULL) {
853                 while ((de = readdir(dir)) != NULL) {
854
855                         /* skip files and dirs starting with '.' or 'bpy' */
856                         if ((de->d_name[0] == '.') || !strncmp(de->d_name, "bpy", 3)) {
857                                 continue;
858                         }
859                         
860                         BLI_make_file_string("/", path, dirname, de->d_name);
861                         
862                         if (stat(path, &status) != 0) {
863                                 if (DEBUG)
864                                         fprintf(stderr, "stat %s failed: %s\n", path, strerror(errno));
865                         }
866
867                         if (S_ISREG(status.st_mode)) { /* is file */
868
869                                 file_extension = strstr(de->d_name, ".py");
870
871                                 if (file_extension && *(file_extension + 3) == '\0') {
872                                         file = fopen(path, "rb");
873
874                                         if (file) {
875                                                 s = de->d_name;
876                                                 if (parentdir) {
877                                                         /* Join parentdir and de->d_name */
878                                                         BLI_join_dirfile(subdir, parentdir, de->d_name);
879
880                                                         s = subdir;
881                                                 }
882                                                 bpymenu_ParseFile(file, s, is_userdir);
883                                                 fclose(file);
884                                         }
885
886                                         else {
887                                                 if (DEBUG)
888                                                         fprintf(stderr, "BPyMenus error: Couldn't open %s.\n", path);
889                                         }
890                                 }
891                         }
892
893                         else if (S_ISDIR(status.st_mode)) { /* is subdir */
894                                 Dirs_Number++;
895                                 Dir_Depth++;
896                                 if (Dirs_Number > MAX_DIR_NUMBER) {
897                                         if (DEBUG) {
898                                                 fprintf(stderr, "BPyMenus error: too many subdirs.\n");
899                                         }
900                                         closedir(dir);
901                                         return -1;
902                                 }
903                                 else if (Dir_Depth > MAX_DIR_DEPTH) {
904                                         if (DEBUG)
905                                                 fprintf(stderr,
906                                                         "BPyMenus error: max depth reached traversing dir tree.\n");
907                                         closedir(dir);
908                                         return -1;
909                                 }
910                                 s = de->d_name;
911                                 if (parentdir) {
912                                         /* Join parentdir and de->d_name */
913                                         BLI_join_dirfile(subdir, parentdir, de->d_name);                                        
914                                         s = subdir;
915                                 }
916                                 if (bpymenu_ParseDir(path, s, is_userdir) == -1) {
917                                         closedir(dir);
918                                         return -1;
919                                 }
920                                 Dir_Depth--;
921                         }
922
923                 }
924                 closedir(dir);
925         }
926
927         else { /* open directory stream failed */
928                 if (DEBUG)
929                         fprintf(stderr, "opendir %s failed: %s\n", dirname, strerror(errno));
930                 return -1;
931         }
932
933         return 0;
934 }
935
936 static int bpymenu_GetStatMTime( char *name, int is_file, time_t * mtime )
937 {
938         struct stat st;
939         int result;
940
941         result = stat( name, &st );
942
943         if( result == -1 )
944                 return -1;
945
946         if( is_file ) {
947                 if( !S_ISREG( st.st_mode ) )
948                         return -2;
949         } else if( !S_ISDIR( st.st_mode ) )
950                 return -2;
951
952         *mtime = st.st_mtime;
953
954         return 0;
955 }
956
957 /* BPyMenu_Init:
958  * import the bpython menus data to Blender, either from:
959  * - the BPYMENU_DATAFILE file (?/.blender/Bpymenus) or
960  * - the scripts dir(s), case newer than the datafile (then update the file).
961  * then fill the bpymenu table with this data.
962  * if param usedir != 0, then the data is recreated from the dir(s) anyway.
963 */
964 int BPyMenu_Init( int usedir )
965 {
966         char fname[FILE_MAXDIR];
967         char dirname[FILE_MAXDIR];
968         char upythondir[FILE_MAXDIR];
969         char *upydir = U.pythondir, *sdir = NULL;
970         time_t time_dir1 = 0, time_dir2 = 0, time_file = 0;
971         int stat_dir1 = 0, stat_dir2 = 0, stat_file = 0;
972         int i;
973
974         DEBUG = G.f & G_DEBUG;  /* is Blender in debug mode (started with -d) ? */
975
976         /* init global bpymenu table (it is a list of pointers to struct BPyMenus
977          * for each available group: import, export, etc.) */
978         for( i = 0; i < PYMENU_TOTAL; i++ )
979                 BPyMenuTable[i] = NULL;
980
981         if( DEBUG )
982                 fprintf(stdout, "\nRegistering scripts in Blender menus ...\n\n" );
983
984         if( U.pythondir[0] == '\0') {
985                 upydir = NULL;
986         }
987         else if (strcmp(U.pythondir, "/") == 0 || strcmp(U.pythondir, "//") == 0) {
988                 /* these are not accepted to prevent possible slight slowdowns on startup;
989                  * they should not be used as user defined scripts dir, anyway, also from
990                  * speed considerations, since they'd not be dedicated scripts dirs */
991                 if (DEBUG) fprintf(stderr,
992                         "BPyMenus: invalid user defined Python scripts dir: \"/\" or \"//\".\n");
993                 upydir = NULL;
994         }
995         else {
996                 BLI_strncpy(upythondir, upydir, FILE_MAXDIR);
997                 BLI_convertstringcode(upythondir, G.sce, 0);
998         }
999
1000         sdir = bpy_gethome(1);
1001
1002         if (sdir) {
1003                 BLI_strncpy(dirname, sdir, FILE_MAXDIR);
1004                 stat_dir1 = bpymenu_GetStatMTime( dirname, 0, &time_dir1 );
1005
1006                 if( stat_dir1 < 0 ) {
1007                         time_dir1 = 0;
1008                         if( DEBUG ) {
1009                                 fprintf(stderr,
1010                                         "\nDefault scripts dir: %s:\n%s\n", dirname, strerror(errno));
1011                                 if( upydir )
1012                                         fprintf(stdout,
1013                                                 "Getting scripts menu data from user defined dir: %s.\n",
1014                                                 upythondir );
1015                         }
1016                 }
1017         }
1018         else stat_dir1 = -1;
1019
1020         if( upydir ) {
1021                 stat_dir2 = bpymenu_GetStatMTime( upythondir, 0, &time_dir2 );
1022
1023                 if( stat_dir2 < 0 ) {
1024                         time_dir2 = 0;
1025                         upydir = NULL;
1026                         if( DEBUG )
1027                                 fprintf(stderr, "\nUser defined scripts dir: %s:\n%s.\n",
1028                                         upythondir, strerror( errno ) );
1029                         if( stat_dir1 < 0 ) {
1030                                 if( DEBUG )
1031                                         fprintf(stderr, "\
1032 To have scripts in menus, please add them to the default scripts dir:\n\
1033 %s\n\
1034 and / or go to 'Info window -> File Paths tab' and set a valid path for\n\
1035 the user defined Python scripts dir.\n", dirname );
1036                                 return -1;
1037                         }
1038                 }
1039         }
1040         else stat_dir2 = -1;
1041
1042         if( ( stat_dir1 < 0 ) && ( stat_dir2 < 0 ) ) {
1043                 if( DEBUG ) {
1044                         fprintf(stderr, "\nCannot register scripts in menus, no scripts dir"
1045                                                         " available.\nExpected default dir at: %s \n", dirname );
1046                 }
1047                 return -1;
1048         }
1049
1050         if (usedir) stat_file = -1;
1051         else { /* if we're not forced to use the dir */
1052                 char *homedir = bpy_gethome(0);
1053
1054                 if (homedir) {
1055                         BLI_make_file_string( "/", fname, homedir, BPYMENU_DATAFILE );
1056                         stat_file = bpymenu_GetStatMTime( fname, 1, &time_file );
1057                         if( stat_file < 0 )
1058                                 time_file = 0;
1059
1060                 /* comparing dates */
1061
1062                         if((stat_file == 0)
1063                                 && (time_file > time_dir1) && (time_file > time_dir2))
1064                         {       /* file is newer */
1065                                 stat_file = bpymenu_CreateFromFile(  ); /* -1 if an error occurred */
1066                                 if( !stat_file && DEBUG )
1067                                         fprintf(stdout,
1068                                                 "Getting menu data for scripts from file:\n%s\n\n", fname );
1069                         }
1070                         else stat_file = -1;
1071                 }
1072                 else stat_file = -1;    /* -1 to use dirs: didn't use file or it was corrupted */
1073         }
1074
1075         if( stat_file == -1 ) { /* use dirs */
1076                 if( DEBUG ) {
1077                         fprintf(stdout,
1078                                 "Getting menu data for scripts from dir(s):\ndefault: %s\n", dirname );
1079                         if( upydir )
1080                                 fprintf(stdout, "user defined: %s\n", upythondir );
1081                         fprintf(stdout, "\n");
1082                 }
1083                 if( stat_dir1 == 0 ) {
1084                         i = bpymenu_ParseDir( dirname, NULL, 0 );
1085                         if (i == -1 && DEBUG)
1086                                 fprintf(stderr, "Default scripts dir does not seem valid.\n\n");
1087                 }
1088                 if( stat_dir2 == 0 ) {
1089                         BLI_strncpy(dirname, U.pythondir, FILE_MAXDIR);
1090                         BLI_convertstringcode(dirname, G.sce, 0);
1091                         i = bpymenu_ParseDir( dirname, NULL, 1 );
1092                         if (i == -1 && DEBUG)
1093                                 fprintf(stderr, "User defined scripts dir does not seem valid.\n\n");
1094                 }
1095
1096                 /* check if we got any data */
1097                 for( i = 0; i < PYMENU_TOTAL; i++ )
1098                         if( BPyMenuTable[i] )
1099                                 break;
1100
1101                 /* if we got, recreate the file */
1102                 if( i < PYMENU_TOTAL )
1103                         bpymenu_WriteDataFile(  );
1104                 else if( DEBUG ) {
1105                         fprintf(stderr, "\n\
1106 Warning: Registering scripts in menus -- no info found.\n\
1107 Either your scripts dirs have no .py scripts or the scripts\n\
1108 don't have a header with registration data.\n\
1109 Default scripts dir is:\n\
1110 %s\n", dirname );
1111                         if( upydir )
1112                                 fprintf(stderr, "User defined scripts dir is: %s\n",
1113                                         upythondir );
1114                 }
1115         }
1116
1117         return 0;
1118 }