2 * Copyright (C) 2014 J. Ali Harlow <ali@juiblex.co.uk>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 * http://www.transmissionzero.co.uk/computing/win32-apps-with-mingw/
24 #define _XOPEN_SOURCE 500
31 #include <plover/plover.h>
32 #include <whelk/whelk.h>
40 #define FOF_NO_UI (FOF_SILENT|FOF_NOCONFIRMATION|FOF_NOERRORUI|\
49 /* Under WIN32, g_spawn requires a helper program which we'd rather avoid */
55 LUALIB_API int luaopen_posix(lua_State *L);
63 int verify_and_fix(const char *root)
69 INT_PTR CALLBACK ProgressDialogProc(HWND dialog,UINT msg,WPARAM w_param,
77 progress=GetDlgItem(dialog,IDC_PROGRESS);
78 style=GetWindowLong(progress,GWL_STYLE);
79 SetWindowLong(progress,GWL_STYLE,style|PBS_MARQUEE);
80 SendMessage(progress,PBM_SETMARQUEE,(WPARAM)TRUE,(LPARAM)30);
83 return (INT_PTR)FALSE;
90 unsigned pre_install_thread(void *data)
95 char *install[]={"plover-gtkui",NULL};
97 prefix=plover_pre_install_prefix();
98 s=g_strconcat(prefix,"/var/log/pre-install",NULL);
101 s=g_strconcat(prefix,"/var/lib/razor",NULL);
102 razor_set_database_path(s);
104 if (verify_and_fix(prefix))
110 retval=!plover_install(path,prefix,install,&error);
112 retval=!plover_update(path,prefix,NULL,&error);
115 fprintf(stderr,"%s\n",error->message);
120 PostQuitMessage(retval);
121 PostThreadMessage(main_thread_id,WM_QUIT,retval,0);
122 _endthreadex(retval);
128 * The idea is that if pre_install() fails, update/setup should fall back
129 * to console interfaces.
136 pre_install(const char *argv0)
144 razor_set_lua_loader("posix",(void (*)())luaopen_posix);
145 razor_set_lua_loader("whelk",(void (*)())luaopen_whelk);
146 path=plover_get_program_directory(argv0);
148 retval=(HANDLE)_beginthreadex(NULL,0,pre_install_thread,path,0,NULL);
150 if (pre_install_thread(path))
153 retval=(void *)1; /* Non-NULL to indicate success */
159 int remove_ignore(const char *fpath,const struct stat *sb,int typeflag,
167 gboolean deltree(const char *path)
170 /* Based on g_local_file_trash() */
171 SHFILEOPSTRUCTW op={0};
175 wfilename=g_utf8_to_utf16(path,-1,NULL,&len,NULL);
176 /* SHFILEOPSTRUCT.pFrom is double-zero-terminated */
177 wfilename=g_renew(wchar_t,wfilename,len+2);
182 success=!SHFileOperationW(&op);
183 if (success && op.fAnyOperationsAborted)
188 return nftw(path,remove_ignore,64,FTW_DEPTH|FTW_PHYS);
192 gboolean pre_uninstall(void)
197 razor_set_lua_loader("posix",(void (*)())luaopen_posix);
198 razor_set_lua_loader("whelk",(void (*)())luaopen_whelk);
199 prefix=plover_pre_install_prefix();
200 s=g_strconcat(prefix,"/var/lib/razor",NULL);
201 razor_set_database_path(s);
203 success=plover_remove(NULL,&error);
206 fprintf(stderr,"%s\n",error->message);
213 #if defined(WIN32) && !defined(USE_G_SPAWN)
214 /* Based on glib's g_spawn_win32.c */
217 win32_cmdline_quote(const char *string)
219 const gchar *p=string;
222 gboolean need_dblquotes=FALSE;
225 if (*p==' ' || *p=='\t')
232 while (*pp && *pp=='\\')
240 q=retval=g_malloc(len+need_dblquotes*2+1);
251 while (*pp && *pp=='\\')
265 /* Create a win32-style wide-character argv with suitable quoting */
266 wchar_t **win32_argv_import(char **argv,GError **error)
271 GError *tmp_error=NULL;
272 n=g_strv_length(argv);
273 wargv=g_new(wchar_t *,n+1);
276 s=win32_cmdline_quote(argv[i]);
277 wargv[i]=g_utf8_to_utf16(s,-1,NULL,NULL,&tmp_error);
281 g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
282 "Invalid argument #%d: %s",i,tmp_error->message);
283 g_error_free(tmp_error);
294 gboolean spawn_sync(char **argv,GError **error)
296 wchar_t *wargv0,**wargv;
298 GError *tmp_error=NULL;
299 wargv0=g_utf8_to_utf16(argv[0],-1,NULL,NULL,&tmp_error);
302 fprintf(stderr,"Conversion error in post\n");
303 g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
304 "Invalid program name: %s",tmp_error->message);
305 g_error_free(tmp_error);
308 wargv=win32_argv_import(argv,error);
311 fprintf(stderr,"Conversion error in post\n");
316 rc=_wspawnvp(P_WAIT,wargv0,(const wchar_t **)wargv);
318 g_strfreev((gchar **)wargv);
319 if (rc==-1 && errno!=0)
321 fprintf(stderr,"Failed to start post command (%s)\n",g_strerror(errno));
322 g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
323 "Failed to execute post command: %s",g_strerror(errno));
326 if (rc!=EXIT_SUCCESS)
328 fprintf(stderr,"post command failed (%ld)\n",(long)rc);
329 g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
330 "Post command exited with code %ld",(long)rc);
336 #endif /* defined(WIN32) && !defined(USE_G_SPAWN) */
339 * Run a command after completing request.
341 * Command may refer to %INSTALL_PREFIX% which will be replaced by the
342 * (first) install prefix used and/or %TEST_RESULT% which will be replaced
343 * bu either "pass" or "fail" depending as to whether the request succeeded
344 * or not. Command may also include double quotes which will be used to
345 * affect how the command is split into arguments much like a shell does.
347 gboolean run_post(int argc,char **argv,gboolean test_result,GError **error)
354 gchar *standard_output,*standard_error;
357 GError *tmp_error=NULL;
360 g_set_error_literal(error,G_FILE_ERROR,G_FILE_ERROR_NOENT,
361 "--post: No command given");
364 printf("Running post command: %s\n",argv[1]);
365 if (!g_shell_parse_argv(argv[1],&post_argc,&post_argv,&tmp_error))
367 g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[1]);
370 for(i=0;i<post_argc;i++)
372 s=strstr(post_argv[i],"%INSTALL_PREFIX%");
376 s+=strlen("%INSTALL_PREFIX%");
377 expanded=g_strconcat(post_argv[i],prefix,s,NULL);
378 g_free(post_argv[i]);
379 post_argv[i]=expanded;
381 s=strstr(post_argv[i],"%TEST_RESULT%");
385 s+=strlen("%TEST_RESULT%");
386 expanded=g_strconcat(post_argv[i],test_result?"pass":"fail",s,NULL);
387 g_free(post_argv[i]);
388 post_argv[i]=expanded;
392 if (!g_spawn_sync(NULL,post_argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL,
393 &standard_output,&standard_error,&exit_status,&tmp_error))
395 fprintf(stderr,"Failed to start post command\n");
396 g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]);
399 if (standard_output && *standard_output)
401 printf("Output from post command %s:\n",post_argv[0]);
402 fputs(standard_output,stdout);
404 g_free(standard_output);
405 if (standard_error && *standard_error)
407 printf("Error output from post command %s:\n",post_argv[0]);
408 fputs(standard_error,stdout);
410 g_free(standard_error);
411 if (!g_spawn_check_exit_status(exit_status,&tmp_error))
413 fprintf(stderr,"post command failed\n");
414 g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]);
418 if (!spawn_sync(post_argv,&tmp_error))
420 g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]);
428 DWORD win32_pre_install_gui(char *argv0)
431 INITCOMMONCONTROLSEX icc={0,};
434 main_thread_id=GetCurrentThreadId();
435 thread=(HANDLE)pre_install(argv0);
438 icc.dwSize=sizeof(icc);
439 icc.dwICC=ICC_WIN95_CLASSES;
440 InitCommonControlsEx(&icc);
441 DialogBox(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_PROGRESSDIALOG),NULL,
442 &ProgressDialogProc);
443 while(GetMessage(&msg,NULL,0,0)>0)
445 TranslateMessage(&msg);
446 DispatchMessage(&msg);
448 WaitForSingleObject(thread,INFINITE);
449 GetExitCodeThread(thread,&retval);
455 int main(int argc,char **argv)
461 * pre-inst is normally a GUI application, but rpm scripts may well
462 * call console applications and it looks ugly if console windows keep
463 * popping up. Avoid this by allocating our own console and hiding it.
465 * - If pre-inst is a console application (typically for debugging),
466 * then skip this step.
467 * - Call ShowWindow twice to negate special handling on first call.
469 if (!GetConsoleWindow())
472 ShowWindow(GetConsoleWindow(),SW_HIDE);
473 ShowWindow(GetConsoleWindow(),SW_HIDE);
476 plover_exception_handler_init();
477 if (argc>1 && !strcmp(argv[1],"-u"))
479 success=pre_uninstall();
485 success=win32_pre_install_gui(argv[0])==EXIT_SUCCESS;
487 success=!!pre_install(argv[0]);
489 if (argc>1 && !strcmp(argv[1],"--post") &&
490 !run_post(argc-1,argv+1,success,&error))
493 fprintf(stderr,"Error in post: %s\n",error->message);
495 MessageBox(NULL,error->message,"Error in post",MB_ICONERROR|MB_OK);
501 return success?EXIT_SUCCESS:EXIT_FAILURE;