Support parallel installations. The idea is that for CAD screener, we want
to be able to install this on the same machine as a standard AVOT setup
(most notably for John's laptop). To allow for the possibility of a second
application that might have the same requirements, we add the concept of
vendor-specific distributions. Thus we can have one distribution for CAD
screener and one for The Next Big Thing. It doesn't seem trivial to have
both CAD screener and AVOT under the same vendor tag so we'll have to have
AVOT under "City Occupational" and CAD screener under "City Occupational Ltd"
or some such kludge.
Most of this is done although we are very short of test cases (in particular
we don't test that it's actually possible to install CAD screener in parallel
with AVOT or to update either of them once installed, which is fundamental).
We also have a lot of baggage left over, including an intercept of razor_set.
The problem that this was introduced to debug has been fixed but it looks
like there are a number of memory leaks which it might be useful to help
track down so it has been left in place for now.
There is still a lot of confusion in plover between path-based and URI-based
API. We should review the API, decide what we want and have a general clear up.
There is also confusion as to the purpose of RAZOR_ROOT (and meaning; path or
URI). This is not used at all in librazor (although it is used in razor.exe).
Ideally we shouldn't use it in plover or plover-gtk either although again, we
might want to support it or an equivalent in (some of) the various executables.
Work that would still to nice to do for CAD screener:
- uninstall (ideally as an installed program that hooks into Add/Remove programs
but even re-running the installer would be acceptable).
- xz support (smaller packages).
- repomd.xml and xml:base (would be needed for an Internet installer).
- graphical installer.
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 uri=razor_path_to_uri(s);
104 razor_set_database_uri(uri);
106 if (verify_and_fix(prefix))
112 retval=!plover_install(path,prefix,install,&error);
114 retval=!plover_update(path,prefix,NULL,&error);
117 fprintf(stderr,"%s\n",error->message);
122 PostQuitMessage(retval);
123 PostThreadMessage(main_thread_id,WM_QUIT,retval,0);
124 _endthreadex(retval);
130 * The idea is that if pre_install() fails, update/setup should fall back
131 * to console interfaces.
138 pre_install(const char *argv0)
146 razor_set_lua_loader("posix",(void (*)())luaopen_posix);
147 razor_set_lua_loader("whelk",(void (*)())luaopen_whelk);
148 path=plover_get_program_directory(argv0);
150 retval=(HANDLE)_beginthreadex(NULL,0,pre_install_thread,path,0,NULL);
152 if (pre_install_thread(path))
155 retval=(void *)1; /* Non-NULL to indicate success */
161 int remove_ignore(const char *fpath,const struct stat *sb,int typeflag,
169 gboolean deltree(const char *path)
172 /* Based on g_local_file_trash() */
173 SHFILEOPSTRUCTW op={0};
177 wfilename=g_utf8_to_utf16(path,-1,NULL,&len,NULL);
178 /* SHFILEOPSTRUCT.pFrom is double-zero-terminated */
179 wfilename=g_renew(wchar_t,wfilename,len+2);
184 success=!SHFileOperationW(&op);
185 if (success && op.fAnyOperationsAborted)
190 return nftw(path,remove_ignore,64,FTW_DEPTH|FTW_PHYS);
194 gboolean pre_uninstall(void)
199 razor_set_lua_loader("posix",(void (*)())luaopen_posix);
200 razor_set_lua_loader("whelk",(void (*)())luaopen_whelk);
201 prefix=plover_pre_install_prefix();
202 s=g_strconcat(prefix,"/var/lib/razor",NULL);
203 uri=razor_path_to_uri(s);
205 razor_set_database_uri(uri);
207 success=plover_remove(NULL,&error);
210 fprintf(stderr,"%s\n",error->message);
217 #if defined(WIN32) && !defined(USE_G_SPAWN)
218 /* Based on glib's g_spawn_win32.c */
221 win32_cmdline_quote(const char *string)
223 const gchar *p=string;
226 gboolean need_dblquotes=FALSE;
229 if (*p==' ' || *p=='\t')
236 while (*pp && *pp=='\\')
244 q=retval=g_malloc(len+need_dblquotes*2+1);
255 while (*pp && *pp=='\\')
269 /* Create a win32-style wide-character argv with suitable quoting */
270 wchar_t **win32_argv_import(char **argv,GError **error)
275 GError *tmp_error=NULL;
276 n=g_strv_length(argv);
277 wargv=g_new(wchar_t *,n+1);
280 s=win32_cmdline_quote(argv[i]);
281 wargv[i]=g_utf8_to_utf16(s,-1,NULL,NULL,&tmp_error);
285 g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
286 "Invalid argument #%d: %s",i,tmp_error->message);
287 g_error_free(tmp_error);
298 gboolean spawn_sync(char **argv,GError **error)
300 wchar_t *wargv0,**wargv;
302 GError *tmp_error=NULL;
303 wargv0=g_utf8_to_utf16(argv[0],-1,NULL,NULL,&tmp_error);
306 fprintf(stderr,"Conversion error in post\n");
307 g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
308 "Invalid program name: %s",tmp_error->message);
309 g_error_free(tmp_error);
312 wargv=win32_argv_import(argv,error);
315 fprintf(stderr,"Conversion error in post\n");
320 rc=_wspawnvp(P_WAIT,wargv0,(const wchar_t **)wargv);
322 g_strfreev((gchar **)wargv);
323 if (rc==-1 && errno!=0)
325 fprintf(stderr,"Failed to start post command (%s)\n",g_strerror(errno));
326 g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
327 "Failed to execute post command: %s",g_strerror(errno));
330 if (rc!=EXIT_SUCCESS)
332 fprintf(stderr,"post command failed (%ld)\n",(long)rc);
333 g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,
334 "Post command exited with code %ld",(long)rc);
340 #endif /* defined(WIN32) && !defined(USE_G_SPAWN) */
343 * Run a command after completing request.
345 * Command may refer to %INSTALL_PREFIX% which will be replaced by the
346 * (first) install prefix used and/or %TEST_RESULT% which will be replaced
347 * bu either "pass" or "fail" depending as to whether the request succeeded
348 * or not. Command may also include double quotes which will be used to
349 * affect how the command is split into arguments much like a shell does.
351 gboolean run_post(int argc,char **argv,gboolean test_result,GError **error)
358 gchar *standard_output,*standard_error;
361 GError *tmp_error=NULL;
364 g_set_error_literal(error,G_FILE_ERROR,G_FILE_ERROR_NOENT,
365 "--post: No command given");
368 printf("Running post command: %s\n",argv[1]);
369 if (!g_shell_parse_argv(argv[1],&post_argc,&post_argv,&tmp_error))
371 g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[1]);
374 for(i=0;i<post_argc;i++)
376 s=strstr(post_argv[i],"%INSTALL_PREFIX%");
380 s+=strlen("%INSTALL_PREFIX%");
381 expanded=g_strconcat(post_argv[i],prefix,s,NULL);
382 g_free(post_argv[i]);
383 post_argv[i]=expanded;
385 s=strstr(post_argv[i],"%TEST_RESULT%");
389 s+=strlen("%TEST_RESULT%");
390 expanded=g_strconcat(post_argv[i],test_result?"pass":"fail",s,NULL);
391 g_free(post_argv[i]);
392 post_argv[i]=expanded;
396 if (!g_spawn_sync(NULL,post_argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL,
397 &standard_output,&standard_error,&exit_status,&tmp_error))
399 fprintf(stderr,"Failed to start post command\n");
400 g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]);
403 if (standard_output && *standard_output)
405 printf("Output from post command %s:\n",post_argv[0]);
406 fputs(standard_output,stdout);
408 g_free(standard_output);
409 if (standard_error && *standard_error)
411 printf("Error output from post command %s:\n",post_argv[0]);
412 fputs(standard_error,stdout);
414 g_free(standard_error);
415 if (!g_spawn_check_exit_status(exit_status,&tmp_error))
417 fprintf(stderr,"post command failed\n");
418 g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]);
422 if (!spawn_sync(post_argv,&tmp_error))
424 g_propagate_prefixed_error(error,tmp_error,"%s: ",post_argv[0]);
432 DWORD win32_pre_install_gui(char *argv0)
435 INITCOMMONCONTROLSEX icc={0,};
438 main_thread_id=GetCurrentThreadId();
439 thread=(HANDLE)pre_install(argv0);
442 icc.dwSize=sizeof(icc);
443 icc.dwICC=ICC_WIN95_CLASSES;
444 InitCommonControlsEx(&icc);
445 DialogBox(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_PROGRESSDIALOG),NULL,
446 &ProgressDialogProc);
447 while(GetMessage(&msg,NULL,0,0)>0)
449 TranslateMessage(&msg);
450 DispatchMessage(&msg);
452 WaitForSingleObject(thread,INFINITE);
453 GetExitCodeThread(thread,&retval);
459 int main(int argc,char **argv)
465 * pre-inst is normally a GUI application, but rpm scripts may well
466 * call console applications and it looks ugly if console windows keep
467 * popping up. Avoid this by allocating our own console and hiding it.
469 * - If pre-inst is a console application (typically for debugging),
470 * then skip this step.
471 * - Call ShowWindow twice to negate special handling on first call.
473 if (!GetConsoleWindow())
476 ShowWindow(GetConsoleWindow(),SW_HIDE);
477 ShowWindow(GetConsoleWindow(),SW_HIDE);
480 plover_exception_handler_init();
481 if (argc>1 && !strcmp(argv[1],"-u"))
483 success=pre_uninstall();
489 success=win32_pre_install_gui(argv[0])==EXIT_SUCCESS;
491 success=!!pre_install(argv[0]);
493 if (argc>1 && !strcmp(argv[1],"--post") &&
494 !run_post(argc-1,argv+1,success,&error))
497 fprintf(stderr,"Error in post: %s\n",error->message);
499 MessageBox(NULL,error->message,"Error in post",MB_ICONERROR|MB_OK);
505 return success?EXIT_SUCCESS:EXIT_FAILURE;